Simple grid gallery with photo_manager? - flutter

I have been trying to recreate this (at the bottom of the page) but with flutter version 2.12.0 and the package photo_manager. I have tried to automatically do this, but unfortunately, I am not able to resolve the following part to the newer dart syntax.
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.done)
return Stack(
children: <Widget>[
Positioned.fill(
child: Image.memory(
snapshot.data, //wrong data type here
fit: BoxFit.cover,
),
),
In the orignal written code (for 2.7) it worked perfectly fine.

Simply call GridGallery() as a normal widget. Make sure to set up permissions to access the device gallery before. Have a look at the build Widget to configure the grid.
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:photo_manager/photo_manager.dart';
class GridGallery extends StatefulWidget {
final ScrollController? scrollCtr;
const GridGallery({
Key? key,
this.scrollCtr,
}) : super(key: key);
#override
_GridGalleryState createState() => _GridGalleryState();
}
class _GridGalleryState extends State<GridGallery> {
List<Widget> _mediaList = [];
int currentPage = 0;
int? lastPage;
#override
void initState() {
super.initState();
_fetchNewMedia();
}
_handleScrollEvent(ScrollNotification scroll) {
if (scroll.metrics.pixels / scroll.metrics.maxScrollExtent > 0.33) {
if (currentPage != lastPage) {
_fetchNewMedia();
}
}
}
_fetchNewMedia() async {
lastPage = currentPage;
final PermissionState _ps = await PhotoManager.requestPermissionExtend();
if (_ps.isAuth) {
// success
//load the album list
List<AssetPathEntity> albums =
await PhotoManager.getAssetPathList(
onlyAll: true);
print(albums);
List<AssetEntity> media =
await albums[0].getAssetListPaged(size: 60, page: currentPage); //preloading files
print(media);
List<Widget> temp = [];
for (var asset in media) {
temp.add(
FutureBuilder(
future: asset.thumbnailDataWithSize(ThumbnailSize(200, 200)), //resolution of thumbnail
builder:
(BuildContext context, AsyncSnapshot<Uint8List?> snapshot) {
if (snapshot.connectionState == ConnectionState.done)
return Container(
child: Stack(
children: <Widget>[
Positioned.fill(
child: Image.memory(
snapshot.data!,
fit: BoxFit.cover,
),
),
if (asset.type == AssetType.video)
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(right: 5, bottom: 5),
child: Icon(
Icons.videocam,
color: Colors.white,
),
),
),
],
),
);
return Container();
},
),
);
}
setState(() {
_mediaList.addAll(temp);
currentPage++;
});
} else {
// fail
/// if result is fail, you can call `PhotoManager.openSetting();` to open android/ios applicaton's setting to get permission
}
}
#override
Widget build(BuildContext context) {
return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scroll) {
_handleScrollEvent(scroll);
return false;
},
child: GridView.builder(
controller: widget.scrollCtr,
itemCount: _mediaList.length,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
itemBuilder: (BuildContext context, int index) {
return _mediaList[index];
}),
);
}
}

Related

Make a list of Flutter Firebase field

Hi, I want to make a list inside the Flutter Firebase field. I'm creating an id for followers in the Field. In Firebase, there is a collection, user ID and followers id in the field. My encodings are as follows. But I'm not making a list. What are the changes I will make?
Followers_card
class FollowersCard extends StatefulWidget {
final snap;
const FollowersCard({
Key? key,
required this.snap,
}) : super(key: key);
#override
State<FollowersCard> createState() => _FollowersCardState();
}
class _FollowersCardState extends State<FollowersCard> {
List<dynamic> followersList = []; // shouldn't use dynamic
getdata() async {
await FirebaseFirestore.instance
.collection("users")
.doc(FirebaseAuth.instance.currentUser!.uid)
.get()
.then((value) async {
// get followerIds
List<String> follwerIds = List.from(value.data()!['followers']);
// loop through all ids and get associated user object by userID/followerID
for (int i = 0; i < follwerIds.length; i++) {
var followerId = follwerIds[i];
var data = await FirebaseFirestore.instance
.collection("users")
.doc(followerId)
.get();
// push that data into followersList variable as we are going
// to use that in listViewBuilder
followersList.add(data);
}
setState(() {});
});
#override
void initState() {
super.initState();
getdata();
}
}
#override
Widget build(BuildContext context) {
// use the listView builder to render the list of followers card
return SingleChildScrollView(
physics: NeverScrollableScrollPhysics(),
child: ListView.builder(
shrinkWrap: true,
itemCount: followersList.length,
itemBuilder: (context, index) {
var followerItem = followersList[index];
print('photoUrl');
return _buildFollowersCard(
followerItem['photoUrl'], followerItem['username']);
}),
);
}
Widget _buildFollowersCard(String photoUrl, String username) {
return Container(
height: 70,
width: double.infinity,
color: mobileBackgroundColor,
child: Card(
child: Column(children: [
//Header
Container(
height: 40,
width: double.infinity,
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 16,
).copyWith(right: 0),
child: Row(
children: [
CircleAvatar(
radius: 16,
backgroundImage: NetworkImage(
photoUrl,
),
),
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
username,
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
),
],
),
)
]),
),
);
}
}
followers_screen
class FollowersScreen extends StatefulWidget {
const FollowersScreen({Key? key}) : super(key: key);
#override
State<FollowersScreen> createState() => _FollowersScreenState();
}
class _FollowersScreenState extends State<FollowersScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: mobileBackgroundColor,
centerTitle: true,
title: Image.asset(
'Resim/logo.png',
height: 50,
),
),
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection('users').snapshots(),
builder: (context,
AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) => FollowersCard(
snap: snapshot.data!.docs[index].data(),
),
);
},
),
);
}
}
The problem is that _FollowersScreenState.initState is in the wrong place. It's inside the function getdata that it is trying to call. The initState is never called. That's why there is no list being built.
Also, setState is the one that assigns State values. So first, populate a temporary list of followers and then assign it to the State one inside the setState callback.
Below is the fixed snippet code for _FollowersScreenState:
class _FollowersCardState extends State<FollowersCard> {
List<dynamic> followersList = []; // shouldn't use dynamic
getdata() async {
List<dynamic> followers = [];
final currentUserSnapshot = await FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.get();
// get followerIds
List<String> follwerIds =
List.from(currentUserSnapshot.data()!['followers']);
// loop through all ids and get associated user object by userID/followerID
for (int i = 0; i < follwerIds.length; i++) {
var followerId = follwerIds[i];
var data = await FirebaseFirestore.instance
.collection('users')
.doc(followerId)
.get();
// push that data into the temp list variable as we are going
// to use that in to setState
followers.add(data);
}
setState(() => followersList = followers);
}
#override
void initState() {
super.initState();
getdata();
}
...

Flutter : Image.memory widget unable to load

We are trying to create custom gallery page, so that we have followed some tutorial website but facing some issue unable to compile the project.
Tutorial link ::
https://medium.com/#mhstoller.it/how-to-create-a-custom-media-picker-in-flutter-to-select-photos-and-videos-from-the-gallery-988eea477643
class MediaGrid extends StatefulWidget {
#override
_MediaGridState createState() => _MediaGridState();
}
class _MediaGridState extends State<MediaGrid> {
List<Widget> _mediaList = [];
int currentPage = 0;
late int lastPage;
#override
void initState() {
super.initState();
_fetchNewMedia();
}
_handleScrollEvent(ScrollNotification scroll) {
if (scroll.metrics.pixels / scroll.metrics.maxScrollExtent > 0.33) {
if (currentPage != lastPage) {
_fetchNewMedia();
}
}
}
Future<ui.Image> loadImage(Uint8List img) async {
final Completer<ui.Image> completer = Completer();
ui.decodeImageFromList(img, (ui.Image img) {
return completer.complete(img);
});
return completer.future;
}
static Future<ui.Image> bytesToImage(Uint8List imgBytes) async{
ui.Codec codec = await ui.instantiateImageCodec(imgBytes);
ui.FrameInfo frame = await codec.getNextFrame();
return frame.image;
}
_fetchNewMedia() async {
lastPage = currentPage;
final PermissionState _ps = await PhotoManager.requestPermissionExtend();
if (_ps.isAuth) {
// success
//load the album list
List<AssetPathEntity> albums =
await PhotoManager.getAssetPathList(onlyAll: true);
print(albums);
List<AssetEntity> media =
await albums[0].getAssetListPaged(page: currentPage, size: 60);
// await albums[0].getAssetListPaged(currentPage, 60);
List<Widget> temp = [];
for (var asset in media) {
temp.add(
FutureBuilder(
future: asset.thumbnailDataWithSize(const ThumbnailSize(200, 200)),
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Stack(
children: [
// Image.asset(
// "assets/images/gallery.png",
// fit: BoxFit.cover,
// ),
Positioned.fill(
child: Image.memory(
snapshot.data,
fit: BoxFit.cover,
),
),
if (asset.type == AssetType.video)
const Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(right: 5, bottom: 5),
child: Icon(
Icons.videocam,
color: Colors.white,
),
),
),
],
);
}
return Container();
},
),
);
}
setState(() {
_mediaList.addAll(temp);
currentPage++;
});
} else {
// fail
/// if result is fail, you can call `PhotoManager.openSetting();` to open android/ios applicaton's setting to get permission
}
}
#override
Widget build(BuildContext context) {
return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scroll) {
return _handleScrollEvent(scroll);
},
child: GridView.builder(
itemCount: _mediaList.length,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
itemBuilder: (BuildContext context, int index) {
return _mediaList[index];
}),
);
}
}
The following code not working,
Positioned.fill(
child: Image.memory(
snapshot.data,**//Error here**
fit: BoxFit.cover,
),
),
I have updated the dependencies to photo_manager 2.1.2 and changed required method accordingly.
Finally its working after Type Casting it.
snapshot.data as Uint8List,
Positioned.fill(
child: Image.memory(
snapshot.data! as Uint8List,
fit: BoxFit.cover,
),
),

how to change the circular progress indicator after loading the data Flutter

the circular progress indicator dont disappear after loading the data .
this is my code where im using the progress indicator
and when i reach the end of the grid view it should load the other data but
the progress indicator makes the same thing it loads and dont disappear after getting data .
i tried to make a boolean isLoading and tried to change it true or false but couldnt find the place where i can do this
int pageNumber = 1;
String filterName = '';
class ShowsListDesign extends StatefulWidget {
#override
_ShowsListDesignState createState() => _ShowsListDesignState();
}
class _ShowsListDesignState extends State<ShowsListDesign> {
ScrollController controller = ScrollController();
ServicesClass service = ServicesClass();
bool isLoading = false;
#override
void initState() {
controller.addListener(listenScrolling);
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: service.getFilms('posts/$pageNumber/$filterName'),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
return Stack(
alignment: Alignment.bottomCenter,
children: [
GridView.builder(
itemCount: snapshot.data.length,
gridDelegate: const
SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 250,
crossAxisSpacing: 24,
mainAxisSpacing: 24,
childAspectRatio: (3 / 5),
),
controller: controller,
itemBuilder: (context, index) {
return FilmsCard(
image: snapshot.data[index]['thumbnailUrl'],
title: snapshot.data[index]['title'],
year: snapshot.data[index]['year'],
);
},
),
FloatingActionButton(
onPressed: () {
scrollUp();
},
elevation: 24,
backgroundColor: PRIMARY,
child: const Text(
'Scroll Up',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
),
),
),
],
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
);
}
void scrollUp() {
const double start = 0;
controller.animateTo(start,
duration: const Duration(seconds: 1, milliseconds: 50),
curve: Curves.easeIn);
}
void listenScrolling() {
if (controller.position.atEdge) {
final isTop = controller.position.pixels == 0;
if (isTop) {
} else {
setState(() {
pageNumber++;
ShowsListDesign();
});
}
}
}
}
It is possible to get errors or no data on future, it will be better with handling those states.
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return Stack(
alignment: Alignment.bottomCenter,
children: [...],
);
} else if (!snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return const Text("couldn't find any data");
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
} else {
return const Text(" any other");
}
},
More about FutureBuilder class.
You can't use FutureBuilder if it's not a one page load. Try this:
(I don't understand your scrolling mechanism though), also call super.initState when you override
String filterName = '';
class ShowsListDesign extends StatefulWidget {
#override
_ShowsListDesignState createState() => _ShowsListDesignState();
}
class _ShowsListDesignState extends State<ShowsListDesign> {
ScrollController controller = ScrollController();
ServicesClass service = ServicesClass();
bool isLoading = false;
// New
int pageNumber = 1;
List? data;
Future<void> load() async {
setState(() {
isLoading = true;
});
data = await service.getFilms('posts/1/$filterName');
setState(() => isLoading = false);
}
#override
void initState() {
// Make sure you call super.initState() when you override
controller.addListener(listenScrolling);
super.initState();
load();
}
// Also call dispose to remove listener
#override
void dispose() {
controller.removeListener(listener);
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Builder(
builder: (BuildContext context) {
if (data != null) {
return Stack(
alignment: Alignment.bottomCenter,
children: [
GridView.builder(
itemCount: data!.length,
gridDelegate: const
SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 250,
crossAxisSpacing: 24,
mainAxisSpacing: 24,
childAspectRatio: (3 / 5),
),
controller: controller,
itemBuilder: (context, index) {
return FilmsCard(
image: data![index]['thumbnailUrl'],
title: data![index]['title'],
year: data![index]['year'],
);
},
),
FloatingActionButton(
onPressed: () {
scrollUp();
},
elevation: 24,
backgroundColor: PRIMARY,
child: const Text(
'Scroll Up',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
),
),
),
],
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
);
}
void scrollUp() {
const double start = 0;
controller.animateTo(start,
duration: const Duration(seconds: 1, milliseconds: 50),
curve: Curves.easeIn);
}
Future<void> listenScrolling() async {
// Change this depending on the scrolling works ?!
if (controller.position.pixels == controller.position.maxScrollExtent && data != null) {
List new_data = await service.getFilms('posts/${pageNumber + 1}/$filterName');
data.addAll(new_data);
setState(() {
pageNumber++;
});
}
}
}

Provider Object Requests Rebuild During Existing Build

I'm learning Provider and my test app draws images from a Firestore database into a ListView. I'd like a LongPress on any image to make the whole list toggle and redraw with checkbox selector icons, as below, similar to the way the gallery works:
My code works, but it throws an exception on every LongPress stating that "setState() or markNeedsBuild() was called during build," and I'm pulling my hair out trying to figure out how to either delay the ChangeNotifier until the widget tree is built? Or some other way to accomplish this task?
My Provider class simply accepts a List of my PictureModel class and has a toggleSelectors() method which notifies listeners. Here's the code:
class PicturesProvider with ChangeNotifier {
List<PictureModel> _pictures = [];
bool visible = false;
UnmodifiableListView<PictureModel> get allPictures => UnmodifiableListView(_pictures);
UnmodifiableListView<PictureModel> get selectedPictures =>
UnmodifiableListView(_pictures.where((pic) => pic.selected));
void addPictures(List<PictureModel> picList) {
_pictures.addAll(picList);
notifyListeners();
}
void toggleSelectors() {
visible = !visible;
_pictures.forEach((pic) {
pic.selectVisible = visible;
});
notifyListeners();
}
}
I have a SinglePicture UI class that loads a network image into an AspectRatio widget and wraps it with a GestureDetector to toggle the selectors and present them on the top of a Stack widget, like so:
Widget build(BuildContext context) {
int originalHeight, originalWidth;
return AspectRatio(
aspectRatio: pictureModel.aspectRatio,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
FutureBuilder<ui.Image>(
future: _getImage(),
builder: (BuildContext context, AsyncSnapshot<ui.Image> snapshot) {
if (snapshot.hasData) {
ui.Image image = snapshot.data;
originalHeight = image.height;
originalWidth = image.width;
return GestureDetector(
onLongPress: () => Provider.of<PicturesProvider>(context, listen: false).toggleSelectors(),
child: RawImage(
image: image,
fit: BoxFit.cover,
// if portrait image, move down slightly for headroom
alignment: Alignment(0, originalHeight > originalWidth ? -0.2 : 0),
),
);
} else {
return Center(child: CircularProgressIndicator());
}
},
),
Positioned(
left: 10.0,
top: 10.0,
child: pictureModel.selectVisible == false
? Container(
height: 0.0,
width: 0.0,
)
: pictureModel.selected == false
? Icon(
Icons.check_box_outline_blank,
size: 30.0,
color: Colors.white,
)
: Icon(
Icons.check_box,
size: 30.0,
color: Colors.white,
),
)
],
),
);
}
This SinglePicture class is then called from my PicturesList UI class which simply builds a ListView, like so:
class PicturesList extends StatelessWidget {
final List<PictureModel> pictures;
PicturesList({#required this.pictures});
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: pictures.length,
cacheExtent: 3,
itemBuilder: (context, index) {
return SinglePicture(
pictureModel: pictures[index],
);
},
);
}
The whole shebang is then called from a FutureBuilder in my app, which builds the app, like so:
body: FutureBuilder(
future: appProject.fetchProject(), // Snapshot of database
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
// Get all picture URLs from project snapshot
List<dynamic> picUrls = snapshot.data['pictures'].map((pic) => pic['pic_cloud']).toList();
// Create list of PictureModel objects for Provider
List<PictureModel> pictures = picUrls.map((url) => PictureModel(imageUrl: url, imageHeight: 250.0, selectVisible: false)).toList();
// Add list of PictureModel objects to Provider for UI render
context.watch<PicturesProvider>().addPictures(pictures);
return SafeArea(
child: PicturesList(
pictures: context.watch<PicturesProvider>().allPictures,
),
);
} else if (snapshot.hasError) {
print('Error');
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
Please, if anybody has a hint about how I can accomplish this toggle action without throwing exceptions, I'd be very grateful. Thank you in advance!
Thanks to Remi Rousselet for the answer:
I have been using .builder methods wrong since the get-go and now need to go revisit ALL of my code and make sure they are clean.
To make this code work, I moved the Future out of my FutureBuilder and called it in the initState method, per Remi's guidance. I also had to create a new initializer method in my Provider class that did NOT notify listeners, so I could build the list for the first time.
Here are the code snippets to make my images 'selectable' with a LongPress and to be able to individually select them with a tap, as seen in the following image:
My PictureModel:
class PictureModel {
final String imageUrl;
final double aspectRatio;
double imageHeight;
bool selectVisible;
bool selected;
PictureModel({
#required this.imageUrl,
#required this.imageHeight,
this.aspectRatio = 4.0 / 3.0,
this.selectVisible = false,
this.selected = false,
});
#override
String toString() {
return 'Image URL: ${this.imageUrl}\n'
'Image Height: ${this.imageHeight}\n'
'Aspect Ratio: ${this.aspectRatio}\n'
'Select Visible: ${this.selectVisible}\n'
'Selected: ${this.selected}\n';
}
}
My PictureProvider model:
class PicturesProvider with ChangeNotifier {
List<PictureModel> _pictures = [];
bool visible = false;
UnmodifiableListView<PictureModel> get allPictures => UnmodifiableListView(_pictures);
UnmodifiableListView<PictureModel> get selectedPictures =>
UnmodifiableListView(_pictures.where((pic) => pic.selected));
void initialize(List<PictureModel> picList) {
_pictures.addAll(picList);
}
void addPictures(List<PictureModel> picList) {
_pictures.addAll(picList);
notifyListeners();
}
void toggleSelected(int index) {
_pictures[index].selected = !_pictures[index].selected;
notifyListeners();
}
void toggleSelectors() {
this.visible = !this.visible;
_pictures.forEach((pic) {
pic.selectVisible = visible;
});
notifyListeners();
}
}
My SinglePicture UI class:
class SinglePicture extends StatelessWidget {
final PictureModel pictureModel;
const SinglePicture({Key key, this.pictureModel}) : super(key: key);
Future<ui.Image> _getImage() {
Completer<ui.Image> completer = new Completer<ui.Image>();
new NetworkImage(pictureModel.imageUrl).resolve(new ImageConfiguration()).addListener(
new ImageStreamListener(
(ImageInfo image, bool _) {
completer.complete(image.image);
},
),
);
return completer.future;
}
#override
Widget build(BuildContext context) {
int originalHeight, originalWidth;
return AspectRatio(
aspectRatio: pictureModel.aspectRatio,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
FutureBuilder<ui.Image>(
future: _getImage(),
builder: (BuildContext context, AsyncSnapshot<ui.Image> snapshot) {
if (snapshot.hasData) {
ui.Image image = snapshot.data;
originalHeight = image.height;
originalWidth = image.width;
return RawImage(
image: image,
fit: BoxFit.cover,
// if portrait image, move down slightly for headroom
alignment: Alignment(0, originalHeight > originalWidth ? -0.2 : 0),
);
} else {
return Center(child: CircularProgressIndicator());
}
},
),
Positioned(
left: 10.0,
top: 10.0,
child: pictureModel.selectVisible == false
? Container(
height: 0.0,
width: 0.0,
)
: pictureModel.selected == false
? Icon(
Icons.check_box_outline_blank,
size: 30.0,
color: Colors.white,
)
: Icon(
Icons.check_box,
size: 30.0,
color: Colors.white,
),
)
],
),
);
}
}
My PicturesList UI class:
class PicturesList extends StatelessWidget {
PicturesList(this.listOfPics);
final List<PictureModel> listOfPics;
#override
Widget build(BuildContext context) {
context.watch<PicturesProvider>().initialize(listOfPics);
final List<PictureModel> pictures = context.watch<PicturesProvider>().allPictures;
return ListView.builder(
itemCount: pictures.length,
cacheExtent: 3,
itemBuilder: (context, index) {
return GestureDetector(
onLongPress: () => Provider.of<PicturesProvider>(context, listen: false).toggleSelectors(),
onTap: () {
if (Provider.of<PicturesProvider>(context, listen: false).visible) {
Provider.of<PicturesProvider>(context, listen: false).toggleSelected(index);
}
},
child: SinglePicture(
pictureModel: pictures[index],
),
);
},
);
}
}
And last but not least, the FutureBuilder in the app from where all of this was called...
body: FutureBuilder(
future: projFuture,
// ignore: missing_return
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
// Get all picture URLs from project snapshot
List<dynamic> picUrls = snapshot.data['pictures'].map((pic) => pic['pic_cloud']).toList();
// Create list of PictureModel objects for Provider
List<PictureModel> pictures = picUrls
.map((url) => PictureModel(imageUrl: url, imageHeight: 250.0, selectVisible: false))
.toList();
// Add list of PictureModel objects to Provider for UI render
// context.watch<PicturesProvider>().addPictures(pictures);
return SafeArea(
child: PicturesList(pictures),
);
} else if (snapshot.hasError) {
print('error');
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
Sorry for the long follow-up, but I figured I'd try to detail as much as possible how to make this work, in case it is useful to anybody else. Also, if anybody has further suggestions on how to improve this code, PLEASE let me know.
Thanks in advance.

Flutter How to Populate ListView on app launch with sqflite?

I'm trying to display data in a ListView with a FutureBuilder. In debug mode, when I launch the app, no data is displayed, but, if I reload the app (hot Reload or hot Restart), the ListView displays all the data. I already tried several approaches to solve this - even without a FutureBuilder, I still haven't succeeded. If I create a button to populate the ListView, with the same method "_getregistos()", the ListView returns the data correctly.
This is the code I'm using:
import 'package:flutter/material.dart';
import 'package:xxxxx/models/task_model.dart';
import 'package:xxxxx/shared/loading.dart';
class AddTask extends StatefulWidget {
static const id = 'add_task';
#override
_AddTaskState createState() => _AddTaskState();
}
class _AddTaskState extends State<AddTask> {
dynamic tasks;
final textController = TextEditingController();
_getRegistos() async {
List<TaskModel> taskList = await _todoHelper.getAllTask();
// print('DADOS DA tasklist: ${taskList.length}');
return taskList;
}
TaskModel currentTask;
final TodoHelper _todoHelper = TodoHelper();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.all(32),
child: Column(
children: <Widget>[
TextField(
controller: textController,
),
FlatButton(
child: Text('Insert'),
onPressed: () {
currentTask = TaskModel(name: textController.text);
_todoHelper.insertTask(currentTask);
},
color: Colors.blue,
textColor: Colors.white,
),
//
FutureBuilder(
future: _getRegistos(),
builder: (context, snapshot) {
if (snapshot.hasData) {
tasks = snapshot.data;
return ListView.builder(
shrinkWrap: true,
itemCount: tasks == null ? 0 : tasks.length,
itemBuilder: (BuildContext context, int index) {
TaskModel t = tasks[index];
return Card(
child: Row(
children: <Widget>[
Text('id: ${t.id}'),
Text('name: ${t.name}'),
IconButton(
icon: Icon(Icons.delete), onPressed: () {})
],
),
);
},
);
}
return Loading();
}),
],
),
),
);
}
}
Thank you.
You need to use ConnectionState inside your builder. Look at this code template: (Currently your builder returns ListView widget without waiting for the future to complete)
return FutureBuilder(
future: yourFuture(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// future complete
// if error or data is false return error widget
if (snapshot.hasError || !snapshot.hasData) {
return _buildErrorWidget();
}
// return data widget
return _buildDataWidget();
// return loading widget while connection state is active
} else
return _buildLoadingWidget();
},
);
Thanks for your help.
I already implemented ConnectionState in the FutureBuilder and the issue persists.
When I launch the app, I get error "ERROR or No-Data" (is the message I defined in case of error of no-data.
If I click on the FlatButton to call the method "_getTasks()", the same method used in FutureBuilder, everything is ok. The method return data correctly.
This is the code refactored:
import 'package:flutter/material.dart';
import 'package:xxxx/models/task_model.dart';
import 'package:xxxx/shared/loading.dart';
class AddTask extends StatefulWidget {
static const id = 'add_task';
#override
_AddTaskState createState() => _AddTaskState();
}
class _AddTaskState extends State<AddTask> {
final textController = TextEditingController();
Future<List<TaskModel>> _getTasks() async {
List<TaskModel> tasks = await _todoHelper.getAllTask();
print('Tasks data: ${tasks.length}');
return tasks;
}
TaskModel currentTask;
//list to test with the FlatButton List all tasks
List<TaskModel> tasksList = [];
final TodoHelper _todoHelper = TodoHelper();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.all(32),
child: Column(
children: <Widget>[
TextField(
controller: textController,
),
FlatButton(
child: Text('Insert'),
onPressed: () {
currentTask = TaskModel(name: textController.text);
_todoHelper.insertTask(currentTask);
},
color: Colors.blue,
textColor: Colors.white,
),
//when clicking on this flatButton, I can populate the taskList
FlatButton(
child: Text('Show all Tasks'),
onPressed: () async {
List<TaskModel> list = await _getTasks();
setState(() {
tasksList = list;
print(
'TaskList loaded by "flatButton" has ${tasksList.length} rows');
});
},
color: Colors.red,
textColor: Colors.white,
),
//
FutureBuilder(
future: _getTasks(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// future complete
// if error or data is false return error widget
if (snapshot.hasError || !snapshot.hasData) {
return Text('ERROR or NO-DATA');
}
// return data widget
return ListItems(context, snapshot.data);
// return loading widget while connection state is active
} else
return Loading();
},
),
],
),
),
);
}
}
//*****************************************
class ListItems extends StatelessWidget {
final List<TaskModel> snapshot;
final BuildContext context;
ListItems(this.context, this.snapshot);
#override
Widget build(BuildContext context) {
return Expanded(
child: ListView.builder(
itemCount: snapshot == null ? 0 : snapshot.length,
itemBuilder: (context, index) {
TaskModel t = snapshot[index];
return Text(' ${t.id} - ${t.name}');
}),
);
}
}