Why does my asynchronous function freeze the UI? - macos

I'm building a Flutter app to send MIDI SysEx messages using a plugin called flutter_midi_command. It uses a binding to a Swift class to send MIDI messages via CoreMIDI on MacOS and iOS. The overall process in the app is the following:
User selects MIDI device -> User selects file -> File is sent via MIDI.
These steps are implemented in separate screens. The file is read in the update_select widget and passed into the updater widget. There's a LinearProgressIndicator to display feedback to the user.
My problem is, that the progress indicator of the updater screen is not shown while the data is sent. In my understanding, I have to start sending the data after the widget is built because the progress needs to be updated. I think the sending function must be asynchronous because the UI would freeze otherwise. But for some reason the UI still freezes. What am I missing here?
updater.dart (there's the problem)
import 'dart:async';
import 'dart:developer' as dev;
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/scheduler.dart';
import 'package:flutter/material.dart';
import 'package:flutter_midi_command/flutter_midi_command.dart';
import 'oxi_one.dart' as oxiOne;
import 'package:oxi_companion_flttr/common/oxi_theme.dart';
class Updater extends StatefulWidget {
#override
_UpdaterState createState() => _UpdaterState();
}
class _UpdaterState extends State<Updater> {
MidiCommand _midiCommand = MidiCommand();
double _progress = 0;
#override
void initState() {
super.initState();
WidgetsBinding.instance?.addPostFrameCallback((_) async {
final Uint8List _syxData = ModalRoute.of(context)?.settings.arguments as Uint8List;
send(_syxData).then((value) => null).whenComplete(() => Navigator.of(context).pushNamedAndRemoveUntil('/update_succeeded', (_)=>(false)));
});
void dispose() {
_midiCommand.disconnectDevice(oxiOne.OxiOne().device);
super.dispose();
}
Future<void> send(Uint8List _syxData) async {
_midiCommand.connectToDevice(oxiOne.OxiOne().device);
int chunkSize = _syxData.indexWhere((element) => element == 0xf7) + 1;
int chunks = (_syxData.lengthInBytes % chunkSize == 0) ?
_syxData.lengthInBytes ~/ chunkSize :
_syxData.lengthInBytes ~/ chunkSize + 1;
for (int chunkCount = 1, start = 0, end = chunkSize; chunkCount <= chunks; chunkCount++ ){
if (chunkCount == chunks){
dev.log("sending last chunk: ");
_midiCommand.sendData(_syxData.sublist(start));
} else {
_midiCommand.sendData(_syxData.sublist(start, end));
dev.log("last byte of this chunk $chunkCount is ${_syxData[end-1]}");
}
start = chunkCount*chunkSize;
end = start+chunkSize;
sleep(Duration(milliseconds: 190));
if (chunkCount == chunks) {
return;
} else {
setState(() {
_progress = 100/chunks*chunkCount;
});
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: OxiAppBar(
title: Text(
"Installing update",
style: TextStyle(
fontWeight: FontWeight.w100,
fontSize: 24
),
),
disableNav: true
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
alignment: Alignment.center,
height: 80.0,
padding: const EdgeInsets.only(left: 40, right: 40),
child: LinearProgressIndicator(
value: _progress,
minHeight: 8,
),
),
Text(
"Update in progress, hold your breath!",
style: TextStyle(
fontSize: 12.0
),
)
],
),
),
);
}
}
update_select.dart (passes the data as Uint8List to the updater)
import 'dart:developer' as dev;
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:file_picker/file_picker.dart' as mobilePicker;
import 'package:file_picker_desktop/file_picker_desktop.dart' as desktopPicker;
import 'oxi_one.dart' as oxiOne;
import 'package:oxi_companion_flttr/common/oxi_theme.dart';
class UpdateSelect extends StatefulWidget {
#override
_UpdateSelectState createState() => _UpdateSelectState();
}
class _UpdateSelectState extends State<UpdateSelect> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: OxiAppBar(
title: Text(
"Install Update",
style: TextStyle(
fontWeight: FontWeight.w100,
fontSize: 24
),
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
children: [
Container(
alignment: Alignment.center,
height: 80.0,
child: Text("Your Oxi One firmware is up2date!"),
)
],
mainAxisAlignment: MainAxisAlignment.center,
),
Row(
children: [
TextButton(
onPressed: _selectUpdate,
child: Text(
"I feel adventurous!",
style: TextStyle(
fontSize: 12.0
),
)
)
],
mainAxisAlignment: MainAxisAlignment.center,
)
],
),
)
);
}
void _selectUpdate() async {
Uint8List? syxData;
if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) {
desktopPicker.FilePickerResult? result = await desktopPicker.pickFiles(
dialogTitle: "Select update file",
allowedExtensions: ["syx"],
type: desktopPicker.FileType.custom,
withData: true
);
if (result != null) {
syxData = result.files.first.bytes as Uint8List;
} else {
Navigator.of(context).pushNamedAndRemoveUntil('/update_file_fail', (_)=>(false));
}
} else {
mobilePicker.FilePickerResult? result = await mobilePicker.FilePicker.platform.pickFiles(
type: mobilePicker.FileType.any,
withData: true
);
if (result != null) {
syxData = result.files.single.bytes as Uint8List;
} else {
Navigator.of(context).pushNamedAndRemoveUntil('/update_file_fail', (_)=>(false));
}
}
Navigator.of(context).pushNamedAndRemoveUntil('/updater', (_)=>(false), arguments: syxData);
}
}

Related

How to search for and connect to a scanner in flutter windows app?

Iam trying to make a flutter windows application that can connect to a scanner device and get the image data from it, but i can't seem to find any packages that help with that so if anyone can give me the link of a library or a tutorial that can help with that.
Thanks in advance.
i have tried searching but all the packages i have found are directed to android and ios only.
Is this what you want?
I've ported the C++ code from https://github.com/twain/twain-samples and created an open source project https://github.com/yushulx/flutter_twain_scanner.
The plugin is available on https://pub.dev/packages/flutter_twain_scanner
The full code to scan documents from a connected scanner on Windows desktop:
import 'dart:io';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_twain_scanner/flutter_twain_scanner.dart';
import 'dart:ui' as ui;
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
final _flutterTwainScannerPlugin = FlutterTwainScanner();
String? _documentPath;
List<String> _scanners = []; // Option 2
String? _selectedScanner;
#override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
// We also handle the message potentially returning null.
try {
platformVersion = await _flutterTwainScannerPlugin.getPlatformVersion() ??
'Unknown platform version';
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter TWAIN Scanner'),
),
body: Stack(
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
height: 100,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
MaterialButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: () async {
List<String>? scanners =
await _flutterTwainScannerPlugin
.getDataSources();
if (scanners != null) {
setState(() {
_scanners = scanners;
});
}
},
child: const Text('List Scanners')),
DropdownButton(
hint: Text(
'Select a scanner'), // Not necessary for Option 1
value: _selectedScanner,
onChanged: (newValue) {
setState(() {
_selectedScanner = newValue;
});
},
items: _scanners.map((location) {
return DropdownMenuItem(
child: new Text(location),
value: location,
);
}).toList(),
),
MaterialButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: () async {
if (_selectedScanner != null) {
int index =
_scanners.indexOf(_selectedScanner!);
String? documentPath =
await _flutterTwainScannerPlugin
.scanDocument(index);
setState(() {
_documentPath = documentPath;
});
}
},
child: const Text('Scan Document')),
]),
),
SizedBox(
height: 600,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: _documentPath == null
? Image.asset('images/default.png')
: Image.file(
File(_documentPath!),
fit: BoxFit.contain,
width: 600,
height: 600,
),
))
],
),
],
),
),
);
}
}

FileImage can't be assigned to parameter

So I've trying to make multiple upload picture by having these:
_selectImage(ImagePicker().getImage(source: ImageSource.gallery), 1);
_selectImage(ImagePicker().getImage(source: ImageSource.gallery), 2);
_selectImage(ImagePicker().getImage(source: ImageSource.gallery), 3);
When method of this:
void _selectImage(Future<PickedFile> image, int imageNumber) async{
FileImage tempImg = (await image) as FileImage;
switch(imageNumber){
case 1: setState(() => _image1 = tempImg);
break;
case 2: setState(() => _image3 = tempImg);
break;
case 2: setState(() => _image3 = tempImg);
break;
}
}
but it says 'FileImage' can't be assigned to parameter type 'File' at here:
Widget _displayChild() {
if (_image1 == null){
return Padding(
padding: const EdgeInsets.fromLTRB(15.0, 20.0, 15.0, 20.0),
child: new Icon(Icons.add, color: grey,),
);
}else{
return Padding(
padding: const EdgeInsets.fromLTRB(15.0, 20.0, 15.0, 20.0),
child: Image.file(_image1)
);
}
}
Can any1 help? This newer version confuses me
It seems you maybe misunderstand the file that should be assigned so I took a few minutes to implement this into a quick main.dart file and get it working for you.
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
void main() {
runApp(
MaterialApp(
home: MyWidget(),
),
);
}
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
File _image1;
File _image2;
File _image3;
void _selectImage(int imageNumber) async {
final PickedFile pickedFile = await ImagePicker().getImage(source: ImageSource.gallery);
switch (imageNumber) {
case 1:
setState(() => _image1 = File(pickedFile.path));
break;
case 2:
setState(() => _image3 = File(pickedFile.path));
break;
case 2:
setState(() => _image3 = File(pickedFile.path));
break;
}
}
Widget _displayChild() {
if (_image1 == null) {
return RaisedButton(
padding: const EdgeInsets.fromLTRB(15.0, 20.0, 15.0, 20.0),
child: new Icon(Icons.add, color: Colors.blue),
onPressed: () => _selectImage(1),
);
} else {
return Padding(
padding: const EdgeInsets.fromLTRB(15.0, 20.0, 15.0, 20.0),
child: Image.file(_image1),
);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Build some widgets!',
style: Theme.of(context).textTheme.headline4,
),
_displayChild(),
],
),
),
);
}
}
Loading an image from a file creates an in memory copy of the file, which is retained in the ImageCache. The underlying file is not monitored for changes. If it does change, the application should evict the entry from the ImageCache.

Flutter FutureBuilder shows data then disappears when calling Rest API (Backend Laravel)

I'm running a flutter application with a laravel backend and I have some issues.
The problem is the FutureBuilder show data then it disappears; Sometimes length==4 then it turns to 0 and shows 'no data' in Scaffold🙄
The same when I refresh the code.
PS: I'm running laravel on localhost and using a real device to test.
Environment: Android Studio, Windows 10, Real device
Laravel project: https://github.com/brakenseddik/blog_api_laravel
Flutter project: https://github.com/brakenseddik/blog_api_flutter
import 'package:http/http.dart' as http;
class Repository {
String _baseUrl = 'http://192.168.1.2:8000/api';
httpGet(String api) async {
return await http.get(_baseUrl + '/' + api);
}
}
here the homepage source code
import 'dart:convert';
import 'package:blog_api/models/post_model.dart';
import 'package:blog_api/services/post_service.dart';
import 'package:flutter/material.dart';
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
PostService _postService = PostService();
List<PostModel> _list = List<PostModel>();
Future<List<PostModel>> _getPosts() async {
var result = await _postService.getAllPosts();
_list = [];
if (result != null) {
var blogPosts = json.decode(result.body);
blogPosts.forEach((post) {
PostModel model = PostModel();
setState(() {
model.title = post['title'];
model.details = post['details'];
model.imageUrl = post['featured_image_url'];
_list.add(model);
});
});
}
return _list;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Blog App'),
),
body: FutureBuilder(
future: _getPosts(),
builder:
(BuildContext context, AsyncSnapshot<List<PostModel>> snapshot) {
print('length of list ${_list.length}');
_list = snapshot.data;
if (_list.length == 0) {
return Center(
child: Text('No data'),
);
} else if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Image.network(
snapshot.data[index].imageUrl,
height: 150,
// width: double.maxFinite,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
snapshot.data[index].title,
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.w700),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
snapshot.data[index].details.substring(0, 25),
style: TextStyle(
fontSize: 16,
),
),
),
],
),
),
);
});
}
},
));
}
}
And the PostService
import 'package:blog_api/repository/repository.dart';
class PostService {
Repository _repository;
PostService() {
_repository = Repository();
}
getAllPosts() async {
return await _repository.httpGet('get-posts');
}
}
I think the issue is that you're calling setState in _getPosts(). This will rebuild everything and never give the response to the FutureBuilder. Just run the code without setState:
blogPosts.forEach((post) {
PostModel model = PostModel();
model.title = post['title'];
model.details = post['details'];
model.imageUrl = post['featured_image_url'];
_list.add(model);
FutureBuilder will initially call its builder with a snapshot that doesn't have any data yet. Once it receives the data, it will call its builder again. Because of this, if (_list.length == 0) will result in a NPE, since _list is null.
I would try changing the order of the if statements:
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.data.length == 0) {
return Center(
child: Text('No data'),
);
} else {
return ListView.builder(
//...
}
future: _getPosts(),
Don't do that. This means every time build is called, you are querying your API again.
Create a variable of type Future<List<PostModel>>, assign _getPosts() to it once and then use that variable with your FutureBuilder.

Flutter: How to put button on each image like (x) to cancel selected image

I am using multi_image_picker 4.6.1 in my application but I faced little problem. How to organize images on specific place on the page and put cancel button on each selected image so user can cancel or remove selected image one by one like in picture here. Thanks in advance
here is the code i am using
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:multi_image_picker/multi_image_picker.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<Asset> images = List<Asset>();
String _error = 'No Error Dectected';
#override
void initState() {
super.initState();
}
Widget buildGridView() {
return GridView.count(
crossAxisCount: 3,
children: List.generate(images.length, (index) {
Asset asset = images[index];
return AssetThumb(
asset: asset,
width: 300,
height: 300,
);
}),
);
}
Future<void> loadAssets() async {
List<Asset> resultList = List<Asset>();
String error = 'No Error Dectected';
try {
resultList = await MultiImagePicker.pickImages(
maxImages: 300,
enableCamera: true,
selectedAssets: images,
cupertinoOptions: CupertinoOptions(takePhotoIcon: "chat"),
materialOptions: MaterialOptions(
actionBarColor: "#abcdef",
actionBarTitle: "Example App",
allViewTitle: "All Photos",
useDetailsView: false,
selectCircleStrokeColor: "#000000",
),
);
} on Exception catch (e) {
error = e.toString();
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
images = resultList;
_error = error;
});
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: const Text('Plugin example app'),
),
body: Column(
children: <Widget>[
Center(child: Text('Error: $_error')),
RaisedButton(
child: Text("Pick images"),
onPressed: loadAssets,
),
Expanded(
child: buildGridView(),
)
],
),
),
);
}
}
Another way to fix using Stack
Stack(
children: <Widget>[
AssetThumb(
asset: asset,
width: 300,
height: 300,
),
Positioned(
right: -2,
top: -9,
child: IconButton(
icon: Icon(
Icons.cancel,
color: Colors.black.withOpacity(0.5),
size: 18,
),
onPressed: () => setState(() {
images.removeAt(index);
})))
],
);
You can try using Stack https://www.youtube.com/watch?v=liEGSeD3Zt8&vl=en
return Stack(
children: <Widget>[
AssetThumb(
asset: asset,
width: 300,
height: 300,
),
Positioned(
top: 0,
right: 0,
child: GestureDetector(
onTap: (){
print('delete image from List');
setState((){
print('set new state of images');
})
},
child: Icon(
Icons.delete,
),
),
),
],
);

How can I select and upload Multiple images in Flutter

I tried to find how to select and upload multiple images in flutter but most of the plugins are not working or I did not understand them well. I found little application but it selects and upload only one picture. How to change this code that user can select and upload multiple pictures or is there any other alternatives. Please write in details, i am freshman in coding. Thanks in advance.
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';
import 'dart:convert';
import 'package:http_parser/http_parser.dart';
import 'package:toast/toast.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: 'Image Upload Demo',
theme: ThemeData(primarySwatch: Colors.pink),
home: ImageInput());
}
}
class ImageInput extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _ImageInput();
}
}
class _ImageInput extends State<ImageInput> {
// To store the file provided by the image_picker
File _imageFile;
// To track the file uploading state
bool _isUploading = false;
String baseUrl = 'http://YOUR_IPV4_ADDRESS/flutterdemoapi/api.php';
void _getImage(BuildContext context, ImageSource source) async {
File image = await ImagePicker.pickImage(source: source);
setState(() {
_imageFile = image;
});
// Closes the bottom sheet
Navigator.pop(context);
}
Future<Map<String, dynamic>> _uploadImage(File image) async {
setState(() {
_isUploading = true;
});
// Find the mime type of the selected file by looking at the header bytes of the file
final mimeTypeData =
lookupMimeType(image.path, headerBytes: [0xFF, 0xD8]).split('/');
// Intilize the multipart request
final imageUploadRequest =
http.MultipartRequest('POST', Uri.parse(baseUrl));
// Attach the file in the request
final file = await http.MultipartFile.fromPath('image', image.path,
contentType: MediaType(mimeTypeData[0], mimeTypeData[1]));
// Explicitly pass the extension of the image with request body
// Since image_picker has some bugs due which it mixes up
// image extension with file name like this filenamejpge
// Which creates some problem at the server side to manage
// or verify the file extension
imageUploadRequest.fields['ext'] = mimeTypeData[1];
imageUploadRequest.files.add(file);
try {
final streamedResponse = await imageUploadRequest.send();
final response = await http.Response.fromStream(streamedResponse);
if (response.statusCode != 200) {
return null;
}
final Map<String, dynamic> responseData = json.decode(response.body);
_resetState();
return responseData;
} catch (e) {
print(e);
return null;
}
}
void _startUploading() async {
final Map<String, dynamic> response = await _uploadImage(_imageFile);
print(response);
// Check if any error occured
if (response == null || response.containsKey("error")) {
Toast.show("Image Upload Failed!!!", context,
duration: Toast.LENGTH_LONG, gravity: Toast.BOTTOM);
} else {
Toast.show("Image Uploaded Successfully!!!", context,
duration: Toast.LENGTH_LONG, gravity: Toast.BOTTOM);
}
}
void _resetState() {
setState(() {
_isUploading = false;
_imageFile = null;
});
}
void _openImagePickerModal(BuildContext context) {
final flatButtonColor = Theme.of(context).primaryColor;
print('Image Picker Modal Called');
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
height: 150.0,
padding: EdgeInsets.all(10.0),
child: Column(
children: <Widget>[
Text(
'Pick an image',
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(
height: 10.0,
),
FlatButton(
textColor: flatButtonColor,
child: Text('Use Camera'),
onPressed: () {
_getImage(context, ImageSource.camera);
},
),
FlatButton(
textColor: flatButtonColor,
child: Text('Use Gallery'),
onPressed: () {
_getImage(context, ImageSource.gallery);
},
),
],
),
);
});
}
Widget _buildUploadBtn() {
Widget btnWidget = Container();
if (_isUploading) {
// File is being uploaded then show a progress indicator
btnWidget = Container(
margin: EdgeInsets.only(top: 10.0),
child: CircularProgressIndicator());
} else if (!_isUploading && _imageFile != null) {
// If image is picked by the user then show a upload btn
btnWidget = Container(
margin: EdgeInsets.only(top: 10.0),
child: RaisedButton(
child: Text('Upload'),
onPressed: () {
_startUploading();
},
color: Colors.pinkAccent,
textColor: Colors.white,
),
);
}
return btnWidget;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Image Upload Demo'),
),
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 40.0, left: 10.0, right: 10.0),
child: OutlineButton(
onPressed: () => _openImagePickerModal(context),
borderSide:
BorderSide(color: Theme.of(context).accentColor, width: 1.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.camera_alt),
SizedBox(
width: 5.0,
),
Text('Add Image'),
],
),
),
),
_imageFile == null
? Text('Please pick an image')
: Image.file(
_imageFile,
fit: BoxFit.cover,
height: 300.0,
alignment: Alignment.topCenter,
width: MediaQuery.of(context).size.width,
),
_buildUploadBtn(),
],
),
);
}
}
I used that package:
dependencies:
multi_image_picker: ^4.6.7
Ex:
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:multi_image_picker/multi_image_picker.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<Asset> images = List<Asset>();
String _error;
#override
void initState() {
super.initState();
}
Widget buildGridView() {
if (images != null)
return GridView.count(
crossAxisCount: 3,
children: List.generate(images.length, (index) {
Asset asset = images[index];
return AssetThumb(
asset: asset,
width: 300,
height: 300,
);
}),
);
else
return Container(color: Colors.white);
}
Future<void> loadAssets() async {
setState(() {
images = List<Asset>();
});
List<Asset> resultList;
String error;
try {
resultList = await MultiImagePicker.pickImages(
maxImages: 300,
);
} on Exception catch (e) {
error = e.toString();
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
images = resultList;
if (error == null) _error = 'No Error Dectected';
});
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: const Text('Plugin example app'),
),
body: Column(
children: <Widget>[
Center(child: Text('Error: $_error')),
RaisedButton(
child: Text("Pick images"),
onPressed: loadAssets,
),
Expanded(
child: buildGridView(),
)
],
),
),
);
}
}
Add dependecy of image_picker:
image_picker: ^0.8.4+3
Then make a method for selectImages():
final ImagePicker imagePicker = ImagePicker();
List<XFile>? imageFileList = [];
void selectImages() async {
final List<XFile>? selectedImages = await
imagePicker.pickMultiImage();
if (selectedImages!.isNotEmpty) {
imageFileList!.addAll(selectedImages);
}
print("Image List Length:" + imageFileList!.length.toString());
setState((){});
}
Create a builder for showing selected Images:
return Scaffold(
appBar: AppBar(
title: Text('Multiple Images'),
),
body: SafeArea(
child: Column(
children: [
ElevatedButton(
onPressed: () {
selectImages();
},
child: Text('Select Images'),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: GridView.builder(
itemCount: imageFileList!.length,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
itemBuilder: (BuildContext context, int index) {
return Image.file(File(imageFileList![index].path),
fit: BoxFit.cover,);
}),
),
),
],
),
));
Complete Source code available in github link...
https://github.com/NishaJain24/multi_image_picker

Resources