need a spinner and dialog box in async API call function - flutter

I have developed an image uploading API and called it an async function I need a spinner to make load until it fetches the result and after fetching the result it has to show the response received in an alert box
uploadImage() async {
final request = http.MultipartRequest(
"POST",
Uri.parse(
'https://f77f-2402-d000-a500-73e5-8c48-4e9a-dad0-c14f.in.ngrok.io/upload'));
final headers = {"Content-type": "multipart/form-data"};
request.files.add(http.MultipartFile("image",
selectedImage!.readAsBytes().asStream(), selectedImage!.lengthSync(),
filename: selectedImage!.path.split("/").last));
request.fields['userId'] = 'value';
request.headers.addAll(headers);
final response = await request.send().then((value) => null);
_isInAsyncCall = true;
http.Response res = await http.Response.fromStream(response);
final resJson = jsonDecode(res.body);
message = resJson['message'];
print(message);
setState(() {
});
My whole flutter code is here
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'find waste collector'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
File? selectedImage;
String? message;
bool _isInAsyncCall = false;
uploadImage() async {
final request = http.MultipartRequest(
"POST",
Uri.parse(
'https://f77f-2402-d000-a500-73e5-8c48-4e9a-dad0-c14f.in.ngrok.io/upload'));
final headers = {"Content-type": "multipart/form-data"};
request.files.add(http.MultipartFile("image",
selectedImage!.readAsBytes().asStream(), selectedImage!.lengthSync(),
filename: selectedImage!.path.split("/").last));
request.fields['userId'] = 'value';
request.headers.addAll(headers);
final response = await request.send().then((value) => null);
_isInAsyncCall = true;
http.Response res = await http.Response.fromStream(response);
final resJson = jsonDecode(res.body);
message = resJson['message'];
print(message);
setState(() {
});
}
Future getImage() async {
final pickedImage =
await ImagePicker().pickImage(source: ImageSource.gallery);
selectedImage = File(pickedImage!.path);
setState(() {});
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
selectedImage == null
? Text("please pick a image to upload")
: Image.file(selectedImage!),
inAsyncCall: _isInAsyncCall,
// demo of some additional parameters
opacity: 0.5,
progressIndicator: CircularProgressIndicator(),
TextButton.icon(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Color.fromARGB(255, 62, 187, 0))),
onPressed: uploadImage,
icon: Icon(Icons.upload_file_rounded, color: Colors.black38),
label: Text("Upload waste sample Image",
style: TextStyle(color: Colors.black)))
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: getImage,
child: Icon(Icons.add_a_photo),
),
// This trailing comma makes auto-formatting nicer for build methods.
);
}
}

modal_progress_hud library helped lot on making this task
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_progress_hud/flutter_progress_hud.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'find waste collector'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
File? selectedImage;
String? message;
bool showSpinner = false;
uploadImage() async {
setState(() {
showSpinner = true;
});
final request = http.MultipartRequest(
"POST",
Uri.parse(
'https://f77f-2402-d000-a500-73e5-8c48-4e9a-dad0-c14f.in.ngrok.io/upload'));
final headers = {"Content-type": "multipart/form-data"};
request.files.add(http.MultipartFile("image",
selectedImage!.readAsBytes().asStream(), selectedImage!.lengthSync(),
filename: selectedImage!.path.split("/").last));
request.fields['userId'] = 'value';
request.headers.addAll(headers);
final response = await request.send();
http.Response res = await http.Response.fromStream(response);
print("reached");
final resJson = jsonDecode(res.body);
message = resJson['message'];
print(message);
setState(() {
showSpinner = false;
});
}
Future getImage() async {
final pickedImage =
await ImagePicker().pickImage(source: ImageSource.gallery);
selectedImage = File(pickedImage!.path);
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
selectedImage == null
? Text("please pick a image to upload")
: Image.file(selectedImage!),
TextButton.icon(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Color.fromARGB(255, 62, 187, 0))),
onPressed: uploadImage,
icon: Icon(Icons.upload_file_rounded, color: Colors.black38),
label: Text("Upload waste sample Image",
style: TextStyle(color: Colors.black)))
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: getImage,
child: Icon(Icons.add_a_photo),
),
);
}
}

Related

Flutter - native_pdf_view - "Looking up a deactivated widget's ancestor is unsafe." when using Navigator.pop

I have a very basic Flutter app with a button to show a PDF. Clicking the button navigates to a new "Viewer" page that uses native_pdf_view to load the PDF. That works well. When I navigate backwards (ie, by tapping the "back" button), I get the error message
The following assertion was thrown while finalizing the widget tree:
Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer stable.
To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
I've looked over a number of other questions that had the same issue (mostly dialogs), and as far as I can tell, they don't apply in this case. I don't think I'm referencing any contexts that have disappeared (but I must be somewhere, I suppose).
When I remove the PdfView widget from the Viewer class (and just have a Column widget containing a 'Back' button), then it doesn't show an error. This seems to imply that the error is somehow caused by native_pdf_view, but nobody else using native_pdf_view seems to have this issue, so I'm sure I'm the problem :) I also suspect that the issue may be with needing a Key somewhere, but I'm unsure where that would be, since as far as I know, I don't have lists of objects with the same type that would benefit from a key.
I would be very grateful for any guidance or hints where to look.
My main.dart file:
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:path/path.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'viewer.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'PDF Viewer Test',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
void _copyAssetPDF(BuildContext context) async {
ByteData data = await rootBundle.load("assets/sample.pdf");
List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
print("Length of sample.pdf: ${data.lengthInBytes}");
Directory appDocDir = await getApplicationDocumentsDirectory();
String path = join(appDocDir.path, "sample.pdf");
await File(path).writeAsBytes(bytes);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () {
// Navigate back to first route when tapped.
_copyAssetPDF(context);
},
child: Text('Copy PDF to local storage'),
),
ElevatedButton(
onPressed: () {
// Navigate back to first route when tapped.
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => Viewer(1)));
},
child: Text('Open PDF From Assets'),
),
ElevatedButton(
onPressed: () {
// Navigate back to first route when tapped.
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => Viewer(2)));
},
child: Text('Open PDF From File'),
),
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
viewer.dart:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:io';
import 'package:path/path.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:native_pdf_view/native_pdf_view.dart';
class Viewer extends StatefulWidget {
final int pdfId;
////const Viewer({Key? key, this.pdfId}) : super(key: key);
//const Viewer({Key? key}) : super(key: key);
Viewer(this.pdfId);
#override
_ViewerState createState() => _ViewerState();
}
class _ViewerState extends State<Viewer> {
String pdfPath = "";
int? pages = 0;
int currentPage = 0;
bool isReady = false;
late final PdfController _pdfController;
#override
void initState() {
//getPdfPathFromDatabase().then((value) {
//setState(() {
// pdfPath = value['path'];
//});
if (widget.pdfId == 1) {
print("Loading PDF from assets...");
_pdfController = PdfController(
document: PdfDocument.openAsset("assets/sample.pdf"),
//document: PdfDocument.openFile(pdfPath),
initialPage: 0,
);
//setState(() {
// isReady = true;
//});
//});
} else if (widget.pdfId == 2) {
print("Loading PDF from openFile...)");
//Load with hard-coded path...
//Load when finding the path at runtime...
getPdfPath().then((value) {
print("Loaded path: ${value['path']}");
_pdfController = PdfController(
document: PdfDocument.openFile(value['path']),
//document: PdfDocument.openFile(pdfPath),
initialPage: 0,
);
setState(() {
isReady = true;
});
});
}
super.initState();
}
Future<Map> getPdfPath() async {
Map res = {};
Directory appDocDir = await getApplicationDocumentsDirectory();
String path = join(appDocDir.path, "sample.pdf");
res['path'] = path;
return res;
}
Future<Map> getPdfPathFromDatabase() async {
Map res = {};
//if (loaded_pdf != null) {
//res['path'] = await loaded_pdf.path_to_pdf();
//}
return res;
}
#override
void dispose() {
print("Disposing pdf controlller..");
_pdfController.dispose();
print("Calling super.dispose...");
super.dispose();
}
#override
Widget build(BuildContext context) {
//if (isReady) {
return Column(
children: <Widget>[
ElevatedButton(
onPressed: () {
// Navigate back to first route when tapped.
print("Going to pop context... ${context}");
Navigator.pop(context);
},
child: Text('Go back!'),
),
Expanded(
child: PdfView(
documentLoader: Center(child: CircularProgressIndicator()),
pageLoader: Center(child: CircularProgressIndicator()),
controller: _pdfController,
onDocumentLoaded: (document) {
pages = document.pagesCount;
print("Document loaded. ${pages} pages");
},
onPageChanged: (page) {
currentPage = page;
},
)
)
]
);
//} else {
// return Text("Not Ready Yet...");
//}
}
}
Try to update the plugin to the new one:
https://pub.dev/packages/pdfx
it will fix the issue
This was a problem using a pre-release version of Flutter (Flutter 2.9.0-pre). When downgrading to Flutter 2.8 (which is stable), the problem goes away.
It's possible the native_pdf_view widget has not been fully upgraded to work with Flutter 2.9 yet, so the solution for now to solve the problem is to use a stable version of Flutter.

notifyListeners() doesn't update the value

I have a MultiProvider in the main with the following code:
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => ReadPreferences(),
),
ChangeNotifierProvider(
create: (context) => ItemsCrud(),
),
],
child: MaterialApp(...
I am using shared preferences to save and updated the last opened list, so the following in my ReadPreferences file:
import 'package:flutter/foundation.dart'; //To use the "ChangeNotifier"
import 'package:shared_preferences/shared_preferences.dart'; //local store
class ReadPreferences extends ChangeNotifier {
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
String openedList = '';
//Constructor method
ReadPreferences() {
getPreferences();
}
void getPreferences() async {
final SharedPreferences prefs = await _prefs;
openedList = prefs.getString('openedList');
}
Future<bool> updateOpenedList({String listTitle}) async {
final SharedPreferences prefs = await _prefs;
bool result = await prefs.setString('openedList', listTitle);
if (result == true) {
openedList = listTitle;
}
notifyListeners();
return result;
}
}
When I'm trying to update the opened list it updates in the shared Preferences file normally but it never listen to the new "openedList" value in my homepage screen.
The code I use in the homepage screen like the following:
child: Text(Provider.of<ReadPreferences>(context).openedList),
I checked many times by printing the new value inside the "ReadPreferences" files, but outside it, it keeps give me the old value not the updated one at all.
I tested with a modified Flutter Counter (default app), everything seams to be working fine. Note that I'm not calling setState() anywhere, so the only refresh is coming from the ReadPreferences class.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ReadPreferences extends ChangeNotifier {
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
String openedList = '';
//Constructor method
ReadPreferences() {
getPreferences();
}
void getPreferences() async {
final SharedPreferences prefs = await _prefs;
openedList = prefs.getString('openedList');
}
Future<bool> updateOpenedList({String listTitle}) async {
final SharedPreferences prefs = await _prefs;
bool result = await prefs.setString('openedList', listTitle);
if (result == true) {
openedList = listTitle;
}
notifyListeners();
return true;
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => ReadPreferences(),
)
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
));
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(Provider.of<ReadPreferences>(context).openedList)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_counter++;
Provider.of<ReadPreferences>(context, listen: false).updateOpenedList(listTitle: (_counter).toString());
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
I finally found the answer, many thanks for #Andrija explanation. What I was doing wrong is to create a new instance from ReadPreferences() then using it for the update method, but the correct approach is to use Provider.of<ReadPreferences>(context, listen: false).updateOpenedList(listTitle: list.title); to use the update method.
For more explanation I'll add #Andrija comment hereafter:-
You are right, you should be using Provider.of. When you add Provider using ChangeNotifierProvider(create: (context) => ReadPreferences(), ) - new instance of ReadPreferences() is created, and it is kept in WidgetTree. This is the instance you want, and you get it by using Provider.of. In your code above, you created a new instance of ReadPreferences - and this is where you added a new value. This new instance has nothing to do with the one that Provider manages, and this new instance has nothing to do with your Widget.

Issue with Creating a Screenshot button for my app using Dart (Flutter)

I am trying to capture a screenshot of the widget and save it to the phone gallery, but it give me error as below. I need help ><
W/System.err(23529): java.io.FileNotFoundException:
/storage/emulated/0/hsp_app/1575363381281.png (No such file or
directory) W/System.err(23529): at
java.io.FileOutputStream.open0(Native Method) W/System.err(23529): at
java.io.FileOutputStream.open(FileOutputStream.java:287)
W/System.err(23529): at
java.io.FileOutputStream.(FileOutputStream.java:223)
W/System.err(23529): at
java.io.FileOutputStream.(FileOutputStream.java:171)
W/System.err(23529): at
com.example.imagegallerysaver.ImageGallerySaverPlugin.saveImageToGallery(ImageGallerySaverPlugin.kt:61)
W/System.err(23529): at
com.example.imagegallerysaver.ImageGallerySaverPlugin.onMethodCall(ImageGallerySaverPlugin.kt:33)
W/System.err(23529): at
io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:222)
W/System.err(23529): at
io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:96)
W/System.err(23529): at
io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:656)
W/System.err(23529): at android.os.MessageQueue.nativePollOnce(Native
Method) W/System.err(23529): at
android.os.MessageQueue.next(MessageQueue.java:325)
W/System.err(23529): at android.os.Looper.loop(Looper.java:142)
W/System.err(23529): at
android.app.ActivityThread.main(ActivityThread.java:6494)
W/System.err(23529): at java.lang.reflect.Method.invoke(Native
Method) W/System.err(23529): at
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
W/System.err(23529): at
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Here's my code:
import 'package:screenshot/screenshot.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
//...
body: new Container(
child: Screenshot(
controller: screenshotController,
// contents...
),),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
_imageFile = null;
screenshotController
.capture()
.then((File image) async {
print("Capture Done");
setState(() {
_imageFile = image;
});
final result =
await ImageGallerySaver.saveImage(image.readAsBytesSync()); // Save image to gallery
print("File Saved to Gallery");
}).catchError((onError) {
print(onError);
});
},
tooltip: 'Capture Result as Screenshot',
icon: Icon(Icons.save),
label: Text("Save")
),
You can copy paste run full code below
You need permission WRITE_EXTERNAL_STORAGE
Step 1: add the permission in AndroidManifest.xml.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Step 2: Use package simple_permissions to get permission
code snippet
#override
void initState() {
super.initState();
_requestWritePermission();
}
_requestWritePermission() async {
PermissionStatus permissionStatus = await SimplePermissions.requestPermission(Permission.WriteExternalStorage);
if (permissionStatus == PermissionStatus.authorized) {
setState(() {
_allowWriteFile = true;
});
}
}
working demo
full code
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:screenshot/screenshot.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:simple_permissions/simple_permissions.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Screenshot Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
File _imageFile;
bool _allowWriteFile = false;
//Create an instance of ScreenshotController
ScreenshotController screenshotController = ScreenshotController();
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
#override
void initState() {
super.initState();
_requestWritePermission();
}
_requestWritePermission() async {
PermissionStatus permissionStatus = await SimplePermissions.requestPermission(Permission.WriteExternalStorage);
if (permissionStatus == PermissionStatus.authorized) {
setState(() {
_allowWriteFile = true;
});
}
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Container(
child: new Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Screenshot(
controller: screenshotController,
child: Column(
children: <Widget>[
Text(
'You have pushed the button this many times:' +
_counter.toString(),
),
FlutterLogo(),
],
),
),
_imageFile != null ? Image.file(_imageFile) : Container(),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_incrementCounter();
_imageFile = null;
screenshotController
.capture(delay: Duration(seconds: 3))
.then((File image) async {
print("image path ${image.path}");
setState(() {
_imageFile = image;
});
final result =
await ImageGallerySaver.saveImage(image.readAsBytesSync());
print("File Saved to Gallery");
}).catchError((onError) {
print(onError);
});
},
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
_saved(File image) async {
final result = await ImageGallerySaver.saveImage(image.readAsBytesSync());
print("File Saved to Gallery");
}
}

Flutter - using PictureRecorder.endRecording().toImage returns blank image

I am trying to create a new Image from two existing images using Canvas
one Img from asset ("asset image")
one Img from network
To achieve that first problem is to draw "asset image" on Canvas using drawImage.. this is where I am facing the problem.
drawCircle is working fine, But for using drawImage as per following code, it is outputting blank image.
I am new to using Canvas and experimenting, any help appreciated..
Complete code..
import 'package:flutter/material.dart';
//import 'package:path_provider/path_provider.dart';
import 'dart:ui' as ui;
import 'dart:typed_data';
import 'dart:async';
//import 'dart:io';
import 'package:flutter/services.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Image _image;
ui.Image imagetoDraw;
#override
void initState() {
super.initState();
_image = new Image.network(
'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png',
);
getImageFromAsset();
}
getImageFromAsset() async {
imagetoDraw = await load('images/loading.png');
print('...getImageFromAsset done');
}
Future<ui.Image> load(String asset) async {
ByteData data = await rootBundle.load(asset);
ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
ui.FrameInfo fi = await codec.getNextFrame();
return fi.image;
}
_generateImage() {
_generate().then((val) => setState(() {
_image = val;
}));
}
Future<Image> _generate() async {
ui.PictureRecorder recorder = new ui.PictureRecorder();
Canvas c = new Canvas(recorder);
var rect = new Rect.fromLTWH(0.0, 0.0, 100.0, 100.0);
c.clipRect(rect);
final paint = new Paint();
paint.strokeWidth = 2.0;
paint.color = const Color(0xFF333333);
paint.style = PaintingStyle.fill;
final offset = new Offset(50.0, 50.0);
// c.drawCircle(offset, 40.0, paint);
c.drawImage(imagetoDraw, offset, paint);
var picture = recorder.endRecording();
final pngBytes = await picture
.toImage(100, 100)
.toByteData(format: ui.ImageByteFormat.png);
var image = Image.memory(pngBytes.buffer.asUint8List());
return image;
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_image,
],
),
),
floatingActionButton: new FloatingActionButton(
onPressed: _generateImage,
tooltip: 'Generate',
child: new Icon(Icons.add),
),
);
}
}
The image asset path seems to be the problem. I can not say that for sure, as There is no pubspec.yaml asset declarations here.
Let's assume that you have added assets in pubspec.yaml as below:
assets:
- assets/images/image_01.png
- assets/images/image_02.jpg
In that case, you need to specify the path of asset 'assets/images/image_01.png'.
Means the exact path that is defined inside the pubspec.yaml file.
i.e. In your case, imagetoDraw = await load('assets/images/image_01.png');
Tip: You can directly use Image.asset('assets/images/image_01.png'); to get the image from assets.
The source I have referred to: Load Image from assets in flutter
I hope this helps.

Flutter reading a file as a stream

I want user to enter text in textfield. When user clicks the fab, the text is written to a file as a new line(appending). I want my app to read the contents of the file and show each line as a listtile in a listview, below the input textfield. When user enters a new text, that should appear in the listview instantaneously.
I was able to do up to writing the text to the file. But how to read the file and display its contents? Should I use streambuilder?. Below is the code I did till now:
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Path Provider',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Path Provider', storage: FileStorage(),),
);
}
}
class FileStorage {
Future<String> get _localPath async {
final directory = await getTemporaryDirectory();
return directory.path;
}
Future<File> get _localFile async {
final path = await _localPath;
return File('$path/file.txt');
}
void readFile() {
/* What to do? */
}
Future<Null> writeFile(String text) async {
final file = await _localFile;
IOSink sink = file.openWrite(mode: FileMode.append);
sink.add(utf8.encode('$text'));
await sink.flush();
await sink.close();
}
}
class MyHomePage extends StatefulWidget {
final FileStorage storage;
MyHomePage({Key key, this.title, this.storage}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final myController = TextEditingController();
#override
void dispose() {
// TODO: implement dispose
myController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text('Testing'),
),
body: new Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: new TextField(
controller: myController,
decoration: new InputDecoration(
hintText: 'Enter the text',
),
),
),
// StreamBuilder(
// stream: widget.storage.readCounter().asStream(),
// )
],
),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.save_alt),
onPressed: () {
widget.storage.writeFile(myController.text);
},
),
);
}
}
An example of reading a file as a stream is actually shown on the File documentation, but the stream ends once you are done reading the file...I don't think it will keep sending you data if you write to it later, but try it out. If you want to observe changes to the file, try using the file.watch function, which returns a Stream of FileSystemEvent. Watch for FileSystemEvent.modify, then each time getting an event, you could call a function to read the file and redisplay all the contents.
This design may be overkill because you could just read the file once on init and keep the state of the list of strings in a state variable or state framework like Redux. Since you are controlling all the writes to the file, barring any errors while writing, your state should be what is saved in the file, so there's no point in reading the file over and over. Here's a sample class that does just that:
import 'dart:async';
import 'dart:io';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
class ReadFileScreen extends StatefulWidget {
#override
ReadFileScreenState createState() {
return new ReadFileScreenState();
}
}
class ReadFileScreenState extends State<ReadFileScreen> {
final myController = TextEditingController();
final storage = FileStorage();
List<String> lines = [];
#override
void initState() {
super.initState();
_loadFile();
}
//can not make initState() async, so calling this function asynchronously
_loadFile() async {
final String readLines = await storage.readFileAsString();
debugPrint("readLines: $readLines");
setState(() {
lines = readLines.split("\\n"); //Escape the new line
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Testing'),
),
body: new Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: new TextField(
controller: myController,
decoration: new InputDecoration(
hintText: 'Enter the text',
),
),
),
new Expanded(
child: new ListView.builder(
itemCount: lines.length,
itemBuilder: (context, index) {
return new Text(lines[index]); //Replace with ListTile here
}),
),
],
),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.save_alt),
onPressed: () {
final String enteredText = myController.text;
storage.writeFile(enteredText);
myController.clear();
setState(() {
lines.add(enteredText);
});
},
),
);
}
}
class FileStorage {
Future<String> get _localPath async {
final directory = await getTemporaryDirectory();
return directory.path;
}
Future<File> get _localFile async {
final path = await _localPath;
return File('$path/file.txt');
}
Future<String> readFileAsString() async {
String contents = "";
final file = await _localFile;
if (file.existsSync()) { //Must check or error is thrown
debugPrint("File exists");
contents = await file.readAsString();
}
return contents;
}
Future<Null> writeFile(String text) async {
final file = await _localFile;
IOSink sink = file.openWrite(mode: FileMode.APPEND);
sink.add(utf8.encode('$text\n')); //Use newline as the delimiter
await sink.flush();
await sink.close();
}
}