How do you insert data into a database in Flutter using the SQFlite plugin?
There are a number of problem solving questions out there but none that I could find to add a canonical answer to. My answer is below.
Add the dependencies
Open pubspec.yaml and in the dependency section add the following lines:
sqflite: ^1.0.0
path_provider: ^0.4.1
The sqflite is the SQFlite plugin of course and the path_provider will help us get the user directory on Android and iPhone.
Make a database helper class
I'm keeping a global reference to the database in a singleton class. This will prevent concurrency issues and data leaks (that's what I hear, but tell me if I'm wrong). You can also add helper methods (like insert) in here for accessing the database.
Create a new file called database_helper.dart and paste in the following code:
import 'dart:io' show Directory;
import 'package:path/path.dart' show join;
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart' show getApplicationDocumentsDirectory;
class DatabaseHelper {
static final _databaseName = "MyDatabase.db";
static final _databaseVersion = 1;
static final table = 'my_table';
static final columnId = '_id';
static final columnName = 'name';
static final columnAge = 'age';
// make this a singleton class
DatabaseHelper._privateConstructor();
static final DatabaseHelper instance = DatabaseHelper._privateConstructor();
// only have a single app-wide reference to the database
static Database _database;
Future<Database> get database async {
if (_database != null) return _database;
// lazily instantiate the db the first time it is accessed
_database = await _initDatabase();
return _database;
}
// this opens the database (and creates it if it doesn't exist)
_initDatabase() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, _databaseName);
return await openDatabase(path,
version: _databaseVersion,
onCreate: _onCreate);
}
// SQL code to create the database table
Future _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE $table (
$columnId INTEGER PRIMARY KEY,
$columnName TEXT NOT NULL,
$columnAge INTEGER NOT NULL
)
''');
}
}
Insert data
We'll use an async method to do our insert:
_insert() async {
// get a reference to the database
// because this is an expensive operation we use async and await
Database db = await DatabaseHelper.instance.database;
// row to insert
Map<String, dynamic> row = {
DatabaseHelper.columnName : 'Bob',
DatabaseHelper.columnAge : 23
};
// do the insert and get the id of the inserted row
int id = await db.insert(DatabaseHelper.table, row);
// show the results: print all rows in the db
print(await db.query(DatabaseHelper.table));
}
Notes
You will have to import the DatabaseHelper class and sqflite if you are in another file (like main.dart).
The SQFlite plugin uses a Map<String, dynamic> to map the column names to the data in each row.
We didn't specify the id. SQLite auto increments it for us.
Raw insert
SQFlite also supports doing a raw insert. This means that you can use a SQL string. Lets insert the same row again using rawInsert().
db.rawInsert('INSERT INTO my_table(name, age) VALUES("Bob", 23)');
Of course, we wouldn't want to hard code those values into the SQL string, but we also wouldn't want to use interpelation like this:
String name = 'Bob';
int age = 23;
db.rawInsert('INSERT INTO my_table(name, age) VALUES($name, $age)'); // Dangerous!
That would open us up to SQL injection attacks. Instead we can use data binding like this:
db.rawInsert('INSERT INTO my_table(name, age) VALUES(?, ?)', [name, age]);
The [name, age] are filled in for the question mark placeholders in (?, ?). The table and column names are safer to use interpelation for, so we could do this finally:
String name = 'Bob';
int age = 23;
db.rawInsert(
'INSERT INTO ${DatabaseHelper.table}'
'(${DatabaseHelper.columnName}, ${DatabaseHelper.columnAge}) '
'VALUES(?, ?)', [name, age]);
Supplemental code
For your copy-and-paste convenience, here is the layout code for main.dart:
import 'package:flutter/material.dart';
import 'package:flutter_db_operations/database_helper.dart';
import 'package:sqflite/sqflite.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'SQFlite Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('sqflite'),
),
body: RaisedButton(
child: Text('insert', style: TextStyle(fontSize: 20),),
onPressed: () {_insert();},
),
);
}
_insert() async {
// get a reference to the database
// because this is an expensive operation we use async and await
Database db = await DatabaseHelper.instance.database;
// row to insert
Map<String, dynamic> row = {
DatabaseHelper.columnName : 'Bob',
DatabaseHelper.columnAge : 23
};
// do the insert and get the id of the inserted row
int id = await db.insert(DatabaseHelper.table, row);
// raw insert
//
// String name = 'Bob';
// int age = 23;
// int id = await db.rawInsert(
// 'INSERT INTO ${DatabaseHelper.table}'
// '(${DatabaseHelper.columnName}, ${DatabaseHelper.columnAge}) '
// 'VALUES(?, ?)', [name, age]);
print(await db.query(DatabaseHelper.table));
}
}
Going on
This post is a development from my previous post: Simple SQFlite database example in Flutter. See that post for other SQL operations and advice.
get in pubspec.yaml
add
dependency:
sqflite: any,
path_provider: any,
intl: ^0.15.7
then click package get and package upgrade
Related
I am trying to figure out how can i watch a StateNotifierProvider and trigger some methods (defined in the class subclassing StateNotifier) on this provider after having done some async computations in another Provider watching the StateNotifierProvider.
Loking at the example below
i need to perform a reset from the RandomAdderNotifierobject provided by the randomAdderProvider if the doneProvider return true.
I try to reset from the doReset Provider. However the provider has nothing to provide.
The point is that both the doneProvider and the doreset provider are not rebuild on state changes of AdderProvider.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:equatable/equatable.dart';
void main() {
runApp(
const ProviderScope(child: MyApp()),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(home: Home());
}
}
final randomProvider = Provider<Random>((ref) {
return Random(1234);
});
//immutable state
class RandomAdder extends Equatable {
final int sum;
const RandomAdder(this.sum);
#override
List<Object> get props => [sum];
}
//State notifier extension
class RandomAdderNotifier extends StateNotifier<RandomAdder> {
RandomAdderNotifier(this.ref) : super(const RandomAdder(0));
final Ref ref;
void randomIncrement() {
state = RandomAdder(state.sum + ref.read(randomProvider).nextInt(5));
}
void reset() {
state = RandomAdder(0);
}
}
/// Providers are declared globally and specify how to create a state
final randomAdderProvider =
StateNotifierProvider<RandomAdderNotifier, RandomAdder>(
(ref) {
return RandomAdderNotifier(ref);
},
);
Future<bool> delayedRandomDecision(ref) async {
int delay = ref.read(randomProvider).nextInt(5);
await Future.delayed(Duration(seconds: delay));
print("You waited $delay seconds for a decision.");
return delay > 4;
}
final doneProvider = FutureProvider<bool>(
(ref) async {
ref.watch(randomAdderProvider);
bool decision = await delayedRandomDecision(ref);
print("the decision is $decision");
return decision;
},
);
final doreset = Provider((ref) {
if (ref.watch(doneProvider).value!) {
ref.read(randomAdderProvider.notifier).reset();
}
});
class Home extends ConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('Counter example')),
body: Center(
// Consumer is a widget that allows you reading providers.
child: Consumer(builder: (context, ref, _) {
final count = ref.watch(randomAdderProvider);
return Text('$count');
}),
),
floatingActionButton: FloatingActionButton(
// The read method is a utility to read a provider without listening to it
onPressed: () =>
ref.read(randomAdderProvider.notifier).randomIncrement(),
child: const Icon(Icons.add),
),
);
}
}
I think ref.listen is more appropriate for usage within the doreset function than ref.watch.
Similarly to ref.watch, it is possible to use ref.listen to observe a provider.
The main difference between them is that, rather than rebuilding the widget/provider if the listened to provider changes, using ref.listen will instead call a custom function.
As per the Riverpod documentation
For ref.listen we need an additional argument - the callback function that we wish to execute on state changes - Source
The ref.listen method needs 2 positional arguments, the first one is the Provider and the second one is the callback function that we want to execute when the state changes.
The callback function when called will be passed 2 values, the value of the previous State and the value of the new State.
&
We will need to handle an AsyncValue - Source
As you can see, listening to a FutureProvider inside a widget returns an AsyncValue – which allows handling the error/loading states.
In Practice
doreset function
I chose to handle the AsyncValue by only handling the data case with state.whenData()
final doReset = Provider<void>(
(ref) {
final done = ref.listen<AsyncValue<bool>>(doneProvider, (previousState, state) {
state.whenData((value) {
if (value) {ref.read(randomAdderProvider.notifier).reset();}
});
});
},
);
Don't forget to watch either doReset/doneProvider in your Home Widget's build method. Without that neither will kick off (Don't have an explanation for this behaviour)
class Home extends ConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
ref.watch(doReset);
...
Lastly, your random function will never meet the condition for true that you have setup as delay>4, as the max possible delay is 4. Try instead using delay>3 or delay=4.
Also perhaps disable the button to prevent clicks while awaiting updates
and in a case where you are using ChangeNotifier You can pass ref in you provider and use the ref same as we can use in ConsumerWidget.
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class YourProvider extends ChangeNotifier {
Ref ref;
YourProvider(this.ref) : super();
callOtherProviderFromThisProvider() {
ref.read(otherProvider).someMethodINeedToTrigger();
}
}
final yourProvider = ChangeNotifierProvider<YourProvider>(
(ref) => YourProvider(ref));
I'm trying to implement BLoC pattern in my flutter app,
basically this app calculate some result and display it in table.
i have created CalculationResultProvider and CalculationResultBloc
as follows
class CalculationResultProvider
{
List<EstimationResult> resultList = new List();
List<EstimationResult> calculateResult(){
return getInitialData(); }
List<EstimationResult> getInitialData(){
var cement = new EstimationResult();
cement.material = "Cement";
cement.unit = "Ton";
cement.qty = 10;
var sand = new EstimationResult();
sand.material = "Sand";
sand.unit = "Ton";
sand.qty = 12;
var gravel = new EstimationResult();
gravel.material = "Gravel";
gravel.unit = "Ton";
gravel.qty = 5;
var steel = new EstimationResult();
steel.material = "Steel";
steel.unit = "Ton";
steel.qty = 5;
List<EstimationResult> resultList = new List();
resultList.add(cement);
resultList.add(sand);
resultList.add(gravel);
resultList.add(steel);
return resultList; } }
and my BLoC provider class as follows
class CalculationResultBloc {
final resultController = StreamController(); // create a StreamController
final CalculationResultProvider provider =
CalculationResultProvider(); // create an instance of our CounterProvider
Stream get getReult =>
resultController.stream; // create a getter for our stream
void updateResult() {
provider
.calculateResult(); // call the method to increase our count in the provider
resultController.sink.add(provider.resultList); // add the count to our sink
}
void dispose() {
resultController.close(); // close our StreamController
}
}
then i need to show this data in table widget
class ResultTableWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() => ResultTableWidgetState();
}
class ResultTableWidgetState extends State {
final bloc =
CalculationResultBloc(); // create an instance of the counter bloc
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: bloc.getReult,
initialData: CalculationResultProvider().getInitialData(),
builder: (context, snapshot) {
DataTable(
columns: [
DataColumn(label: Text('Patch')),
DataColumn(label: Text('Version')),
DataColumn(label: Text('Ready')),
],
rows:
'${snapshot.data}' // Loops through dataColumnText, each iteration assigning the value to element
.map(
((element) => DataRow(
cells: <DataCell>[
DataCell(Text(element[
"Name"])), //Extracting from Map element the value
DataCell(Text(element["Number"])),
DataCell(Text(element["State"])),
],
)),
)
.toList(),
);
});
}
#override
void dispose() {
bloc.dispose();
super.dispose();
}
}
To iterate returning table it should be List<EstimationResult>
but how to transform snapshot in to List<EstimationResult> ?
where is the best place to transform inside bloc class or in widget class ?
im new to dart and flutter , can any one answer my questions?
thanks.
Your widget class will have no clue of the data type given by the stream function in your StreamBuilder, there are many ways to convert data in BloC right before streaming it, but all of them will be useless because to the widget class it's only a snapshot, and the only fields you can access in compilation time are those applied to a generic snapshot like data field. So, The only way to access custom list fields is to provide your StreamBuilder with what type of data to be expected from its stream function :
StreamBuilder<List<EstimationResult>>(
stream: bloc.getReult,
initialData: CalculationResultProvider().getInitialData(),
builder: (context, snapshot) {
//...
}
);
This way you can treat your snapshot as List<EstimationResult>, and have access to inner fields and functions right before you receive the actual snapshot. In your case probably you should import EstimationResult class into your widget class.
I see there are a lot of examples on how to upload an image using flutter to firebase storage but nothing on actually downloading/reading/displaying one that's already been uploaded.
In Android, I simply used Glide to display the images, how do I do so in Flutter? Do I use the NetworkImage class and if so, how do I first get the url of the image stored in Storage?
To view the images inside your storage, what you need is the name of the file in the storage. Once you've the file name for the specific image you need.
In my case if i want the testimage to be loaded,
final ref = FirebaseStorage.instance.ref().child('testimage');
// no need of the file extension, the name will do fine.
var url = await ref.getDownloadURL();
print(url);
Once you've the url,
Image.network(url);
That's all :)
New alternative answer based on one of the comments.
I don't see anywhere google is charging extra money for downloadURL.
So if you're posting some comments please attach a link to it.
Once you upload a file to storage, make that filename unique and save that name somewhere in firestore, or realtime database.
getAvatarUrlForProfile(String imageFileName) async {
final FirebaseStorage firebaseStorage = FirebaseStorage(
app: Firestore.instance.app,
storageBucket: 'gs://your-firebase-app-url.com');
Uint8List imageBytes;
await firebaseStorage
.ref()
.child(imageFileName)
.getData(100000000)
.then((value) => {imageBytes = value})
.catchError((error) => {});
return imageBytes;
}
Uint8List avatarBytes =
await FirebaseServices().getAvatarUrlForProfile(userId);
and use it like,
MemoryImage(avatarBytes)
update
In newer versions use
await ref.getDownloadURL();
See How to get full downloadUrl from UploadTaskSnapshot in Flutter?
original
someMethod() async {
var data = await FirebaseStorage.instance.ref().child("foo$rand.txt").getData();
var text = new String.fromCharCodes(data);
print(data);
}
see Download an image from Firebase to Flutter
or
final uploadTask = imageStore.putFile(imageFile);
final url = (await uploadTask.future).downloadUrl;
In the later case you'd need to store the downloadUrl somewhere and then use NetworkImage or similar to get it rendered.
Here's an example of a stateful widget that loads an image from Firebase Storage object and builds an Image object:
class _MyHomePageState extends State<MyHomePage> {
final FirebaseStorage storage = FirebaseStorage(
app: Firestore.instance.app,
storageBucket: 'gs://my-project.appspot.com');
Uint8List imageBytes;
String errorMsg;
_MyHomePageState() {
storage.ref().child('selfies/me2.jpg').getData(10000000).then((data) =>
setState(() {
imageBytes = data;
})
).catchError((e) =>
setState(() {
errorMsg = e.error;
})
);
}
#override
Widget build(BuildContext context) {
var img = imageBytes != null ? Image.memory(
imageBytes,
fit: BoxFit.cover,
) : Text(errorMsg != null ? errorMsg : "Loading...");
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new ListView(
children: <Widget>[
img,
],
));
}
}
Note that FirebaseApp initialization is handled by the Firestore class, so no further initialization code is necessary.
The way I did it to respect the Storage rules and keep the image in cache is downloading the image as a File and store in the device. Next time I want to display the image I just check if the file already exists or not.
This is my widget:
import 'dart:io';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
class FirebaseImage extends StatefulWidget {
final String storagePath;
FirebaseImage({
required this.storagePath,
}) : super(key: Key(storagePath));
#override
State<FirebaseImage> createState() => _FirebaseImageState();
}
class _FirebaseImageState extends State<FirebaseImage> {
File? _file;
#override
void initState() {
init();
super.initState();
}
Future<void> init() async {
final imageFile = await getImageFile();
if (mounted) {
setState(() {
_file = imageFile;
});
}
}
Future<File?> getImageFile() async {
final storagePath = widget.storagePath;
final tempDir = await getTemporaryDirectory();
final fileName = widget.storagePath.split('/').last;
final file = File('${tempDir.path}/$fileName');
// If the file do not exists try to download
if (!file.existsSync()) {
try {
file.create(recursive: true);
await FirebaseStorage.instance.ref(storagePath).writeToFile(file);
} catch (e) {
// If there is an error delete the created file
await file.delete(recursive: true);
return null;
}
}
return file;
}
#override
Widget build(BuildContext context) {
if (_file == null) {
return const Icon(Icons.error);
}
return Image.file(
_file!,
width: 100,
height: 100,
);
}
}
Note: The code can be improved to show a loading widget, error widget, etc.
In Flutter how to save an image from network to the local directory.
I am new to encoding and decoding images. Can anyone point me in the right direction?
If all you want is to save an image (example: a .png) to the device, you can easily achieve this with a simple get (http/http.dart) and a File (dart:io).
To do so, you can base yourself in the example below:
var response = await http.get(imgUrl);
Directory documentDirectory = await getApplicationDocumentsDirectory();
File file = new File(join(documentDirectory.path, 'imagetest.png'));
file.writeAsBytesSync(response.bodyBytes); // This is a sync operation on a real
// app you'd probably prefer to use writeAsByte and handle its Future
Note that in the case above I’ve used the ‘path_provider’ package from the dart pub. In overall you would have imported at least these items:
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
There's a simple answer and a more complicated answer to this. I'll describe the complicated one and then give you the simple one.
The complicated answer is that you could manually cache images by using a NetworkImage, resolving it, and getting the image stream. Once you have the image stream, you could save it to the filesystem using flutter's file reading & writing capabilities (this is a good resource to learn more about that) - you also need to use a plugin called PathProvider which gets the right path for both iOS and Android which is described in that link. You'd also want to keep track of all the images you'd downloaded and probably delete them after certain amount of time. You'd also have to read the files back before using them to create Image widgets.
That gives you lots of control, but is a bit of work (although not a crazy amount, but if you're new to flutter maybe not something you want to do just now depending on why you want to save the images).
The simple answer is Packages to the rescue! Someone else has already come across this problem and written a plugin that solves it for you, so you don't have to think about it!
See cached_network_image package for information about the plugin.
You need to add to your dependencies in pubspec.yaml
dependencies:
cached_network_image: "^0.3.0"
Import it:
import 'package:cached_network_image/cached_network_image.dart';
And use it!
new CachedNetworkImage(
imageUrl: "http://imageurl.png",
placeholder: new CircularProgressIndicator(),
errorWidget: new Icon(Icons.error),
),
Note that this downloads & shows the image - if you want to do those seperately you can use a new CachedNetworkImageProvider(url) and show it using new Image.
After wandering around the codes and flutter docs. I have found the methods and classes which would work for both iOS and Android and here it goes.
Helper Classs
import 'dart:async';
import 'dart:io' as Io;
import 'package:image/image.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:path_provider/path_provider.dart';
class SaveFile {
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<Io.File> getImageFromNetwork(String url) async {
var cacheManager = await CacheManager.getInstance();
Io.File file = await cacheManager.getFile(url);
return file;
}
Future<Io.File> saveImage(String url) async {
final file = await getImageFromNetwork(url);
//retrieve local path for device
var path = await _localPath;
Image image = decodeImage(file.readAsBytesSync());
Image thumbnail = copyResize(image, 120);
// Save the thumbnail as a PNG.
return new Io.File('$path/${DateTime.now().toUtc().toIso8601String()}.png')
..writeAsBytesSync(encodePng(thumbnail));
}
}
Usage of class
class HomePageState extends State<HomePage>{
Future<Null> _launched ;
Widget _showResult(BuildContext context, AsyncSnapshot<Null> snapshot){
if(!snapshot.hasError){
return Text('Image is saved');
}
else{
return const Text('Unable to save image');
}
}
Future<Null> _saveNetworkImage(String url) async{
try{
await SaveFile().saveImage(url);
}
on Error catch(e){
throw 'Error has occured while saving';
}
}
#override
Widget Build(BuildContext context){
return new Scaffold(
key: _scaffoldKey,
appBar: new AppBar(
title: new Text('Image'),
),
body: Column(
children: <Widget>[
IconButton(icon: Icon(Icons.save), onPressed: (){
setState(() {
_launched =_saveNetworkImage(url);
});
}),
new FutureBuilder<Null>(future: _launched ,builder: _showResult),
],
),
);
}
}
To save the network image in local system you need to use ImagePickerSave dart plugin. Add the dart plugin in pub.yaml file: image_picker_saver: ^0.1.0 and call below code to save the image. URL is the image URL of network image
void _onImageSaveButtonPressed(String url) async {
print("_onImageSaveButtonPressed");
var response = await http
.get(url);
debugPrint(response.statusCode.toString());
var filePath = await ImagePickerSaver.saveFile(
fileData: response.bodyBytes);
var savedFile= File.fromUri(Uri.file(filePath));
}
Follow this url flutter save network image.
Code Snippet
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:http/http.dart' as http;
class NetworkToLocalImage extends StatefulWidget{
String url;
NetworkToLocalImage(this.url);
#override
_LoadImages createState() => new _LoadImages(url);
}
class _LoadImages extends State<NetworkToLocalImage>{
String url;
String filename;
var dataBytes;
_LoadImages(this.url){
filename = Uri.parse(url).pathSegments.last;
downloadImage().then((bytes){
setState(() {
dataBytes = bytes;
});
});
}
Future<dynamic> downloadImage() async {
String dir = (await getApplicationDocumentsDirectory()).path;
File file = new File('$dir/$filename');
if (file.existsSync()) {
print('file already exist');
var image = await file.readAsBytes();
return image;
} else {
print('file not found downloading from server');
var request = await http.get(url,);
var bytes = await request.bodyBytes;//close();
await file.writeAsBytes(bytes);
print(file.path);
return bytes;
}
}
#override
Widget build(BuildContext context) {
// TODO: implement build
if(dataBytes!=null)
return new Image.memory(dataBytes);
else return new CircularProgressIndicator();
}
}
I had a lot of trouble doing this, so I wanted to expound a little on the above answers with a simple example that downloads a file on startup, saves it to a local directory. I marked a couple of lines with the comment //%%% to show lines that can be commented out the second time that the app runs. (because it doesn't need to be downloaded to be displayed... it's already on the device itself:
import 'package:flutter/material.dart';
import 'package:http/http.dart' show get;
import 'dart:io';
import 'package:path_provider/path_provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Test Image',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Test Image'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
initState() {
_downloadAndSavePhoto();
super.initState();
}
_downloadAndSavePhoto() async {
// Get file from internet
var url = "https://www.tottus.cl/static/img/productos/20104355_2.jpg"; //%%%
var response = await get(url); //%%%
// documentDirectory is the unique device path to the area you'll be saving in
var documentDirectory = await getApplicationDocumentsDirectory();
var firstPath = documentDirectory.path + "/images"; //%%%
//You'll have to manually create subdirectories
await Directory(firstPath).create(recursive: true); //%%%
// Name the file, create the file, and save in byte form.
var filePathAndName = documentDirectory.path + '/images/pic.jpg';
File file2 = new File(filePathAndName); //%%%
file2.writeAsBytesSync(response.bodyBytes); //%%%
setState(() {
// When the data is available, display it
imageData = filePathAndName;
dataLoaded = true;
});
}
String imageData;
bool dataLoaded = false;
#override
Widget build(BuildContext context) {
if (dataLoaded) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// imageData holds the path AND the name of the picture.
Image.file(File(imageData), width: 600.0, height: 290.0)
],
),
),
);
} else {
return CircularProgressIndicator(
backgroundColor: Colors.cyan,
strokeWidth: 5,
);
}
}
}
And heres my pubspec.yaml file:
http: ^0.12.0+2
path_provider: 1.5.0
you can use this function to upload and get Url(download) Image from the camera using
firebase-storage
Future _upIm()async {
final StorageReference firebaseStorageRef =
FirebaseStorage.instance.ref().child(DateTime.now().toString());
final StorageUploadTask task =
firebaseStorageRef.putFile(_storedImage);
var downUrl=await (await task.onComplete).ref.getDownloadURL();
var url =downUrl.toString();
print(url);
setState(() {
uploadImage=url;
});
}
inspired by Paras,CacheManager.getInstance() is deprecated
final DefaultCacheManager defaultCacheManager = DefaultCacheManager();
final fileInfo = await defaultCacheManager.getFileFromCache(imageUrl);
ImageGallerySaver.saveImage(image);
below is saveImage function
// save raw data to gallery with permission
static Future<void> saveImage(Uint8List image) async {
try {
if (Platform.isIOS) {
await PermissionHandler().requestPermissions([PermissionGroup.photos]);
PermissionStatus permission = await PermissionHandler().checkPermissionStatus(PermissionGroup.photos);
if (permission == PermissionStatus.granted) {
} else {
throw 'denied';
}
} else if (Platform.isAndroid) {
await PermissionHandler().requestPermissions([PermissionGroup.storage]);
PermissionStatus permission = await PermissionHandler().checkPermissionStatus(PermissionGroup.storage);
if (permission == PermissionStatus.granted) {
} else {
throw 'denied';
}
}
await ImageGallerySaver.saveImage(image);
} catch (e) {
throw e;
}
}
You can use like that
var response = await http.get(Uri.parse(imageUrl));
Directory documentDirectory = await getApplicationDocumentsDirectory();
File file = File(join(documentDirectory.path, 'imagetest.png'));
await GallerySaver.saveImage(file.path);
You can use image_downloader.
For ios, image is saved in Photo Library.
For Android, image is saved in Environment.DIRECTORY_DOWNLOADS or specified location. By calling inExternalFilesDir(), specification of permission becomes unnecessary.
By callback(), you can get progress status.
The following is the simplest example. It will be saved.
await ImageDownloader.downloadImage(url);
I have a list of models that I need to create a mini reflective system.
I analyzed the Serializable package and understood how to create one generated file per file, however, I couldn't find how can I create one file for a bulk of files.
So, how to dynamically generate one file, using source_gen, for a list of files?
Example:
Files
user.dart
category.dart
Generated:
info.dart (containg information from user.dart and category.dart)
Found out how to do it with the help of people in Gitter.
You must have one file, even if empty, to call the generator. In my example, it is lib/batch.dart.
source_gen: ^0.5.8
Here is the working code:
The tool/build.dart
import 'package:build_runner/build_runner.dart';
import 'package:raoni_global/phase.dart';
main() async {
PhaseGroup pg = new PhaseGroup()
..addPhase(batchModelablePhase(const ['lib/batch.dart']));
await build(pg,
deleteFilesByDefault: true);
}
The phase:
batchModelablePhase([Iterable<String> globs =
const ['bin/**.dart', 'web/**.dart', 'lib/**.dart']]) {
return new Phase()
..addAction(
new GeneratorBuilder(const
[const BatchGenerator()], isStandalone: true
),
new InputSet(new PackageGraph.forThisPackage().root.name, globs));
}
The generator:
import 'dart:async';
import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';
import 'package:glob/glob.dart';
import 'package:build_runner/build_runner.dart';
class BatchGenerator extends Generator {
final String path;
const BatchGenerator({this.path: 'lib/models/*.dart'});
#override
Future<String> generate(Element element, BuildStep buildStep) async {
// this makes sure we parse one time only
if (element is! LibraryElement)
return null;
String libraryName = 'raoni_global', filePath = 'lib/src/model.dart';
String className = 'Modelable';
// find the files at the path designed
var l = buildStep.findAssets(new Glob(path));
// get the type of annotation that we will use to search classes
var resolver = await buildStep.resolver;
var assetWithAnnotationClass = new AssetId(libraryName, filePath);
var annotationLibrary = resolver.getLibrary(assetWithAnnotationClass);
var exposed = annotationLibrary.getType(className).type;
// the caller library' name
String libName = new PackageGraph.forThisPackage().root.name;
await Future.forEach(l.toList(), (AssetId aid) async {
LibraryElement lib;
try {
lib = resolver.getLibrary(aid);
} catch (e) {}
if (lib != null && Utils.isNotEmpty(lib.name)) {
// all objects within the file
lib.units.forEach((CompilationUnitElement unit) {
// only the types, not methods
unit.types.forEach((ClassElement el) {
// only the ones annotated
if (el.metadata.any((ElementAnnotation ea) =>
ea.computeConstantValue().type == exposed)) {
// use it
}
});
});
}
});
return '''
$libName
''';
}
}
It seems what you want is what this issue is about How to generate one output from many inputs (aggregate builder)?
[Günter]'s answer helped me somewhat.
Buried in that thread is another thread which links to a good example of an aggregating builder:
1https://github.com/matanlurey/build/blob/147083da9b6a6c70c46eb910a3e046239a2a0a6e/docs/writing_an_aggregate_builder.md
The gist is this:
import 'package:build/build.dart';
import 'package:glob/glob.dart';
class AggregatingBuilder implements Builder {
/// Glob of all input files
static final inputFiles = new Glob('lib/**');
#override
Map<String, List<String>> get buildExtensions {
/// '$lib$' is a synthetic input that is used to
/// force the builder to build only once.
return const {'\$lib$': const ['all_files.txt']};
}
#override
Future<void> build(BuildStep buildStep) async {
/// Do some operation on the files
final files = <String>[];
await for (final input in buildStep.findAssets(inputFiles)) {
files.add(input.path);
}
String fileContent = files.join('\n');
/// Write to the file
final outputFile = AssetId(buildStep.inputId.package,'lib/all_files.txt');
return buildStep.writeAsString(outputFile, fileContent);
}
}