Best practice for showing db imagepicked and asset files in the same GridView - database

I would like to have some insight into what is the best way to go to show images from assets and imagePicked images (stored in db) in the same gridView? I am using a Photo model class with different variables to store the images, assetPath to store assetimages and fileName to store images from db (these are converted to Image.memory through a utillity method). The problem as I see it is that i Store the images in the same List to be able to display them but this raises issues when some images are base64 and others are assetPath (String). whats best practice regarding this?
Here is my test code. I am here trying to check if the photo contains an assetPath and if so its an asset image otherwise its an imagepicked image and then I use utility method and display it as an Image.memory.
When I test the app on a physical device it shows the asset images fine but the database (imagepicked ) files are just empty squares (as it should be because of base64 I suppose).
I can also mention that the imagepicking and database utility methods are working fine so I choose to not include them here. The main issue is as mentioned best practice for showing both asset and imagepicked from database files in the same gridview.
gridView:
gridview2() {
String image1 = ('images/lake.jpg');
String image2 = ('images/flower1.png');
Photo photo1 = Photo(id: 0, fileName: image1);
Photo photo2 = Photo(id: 0, fileName: image2);
images!.add(photo1);
images!.add(photo2);
return Padding(
padding: const EdgeInsets.all(5.0),
child: GridView.builder(
itemCount: images!.length,
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
crossAxisSpacing: 20,
mainAxisSpacing: 20),
itemBuilder: (BuildContext ctx, int index) {
return Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
//todo we need a check if String is database base64 or asset path??
image: images![index].fileName!.isNotEmpty
? Image.asset(images![index].fileName!).image
: Utility.imageFromBase64String(
images![index].fileName!)
.image),
));
}));
}
Scaffold:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Center(child: Text('ImagePicker Database test')),
actions: <Widget>[
IconButton(
icon: Icon(Icons.add),
onPressed: () {
setState(() {
pickImagesFromGallery();
});
},
),
],
),
body: Column(
children: <Widget>[
Flexible(child: gridview2()),
],
),
);
}
}
initState function:
void initState() {
super.initState();
images = [];
dbHelper = DBHelper();
refreshImages();
}
RefreshImage function:
refreshImages() {
dbHelper!.getPhotos().then((imgs) {
setState(() {
//Clears list
images!.clear();
//Add all images from imgs (database) to images list
images!.addAll(imgs);
});
});
}
From physical phone when loaded and added one picked file (becomes empty):

I would adjust your Photo model to have separate fields for assetPath and base64String. This way you can know for sure what the image data looks like.
DecorationImage(
fit: BoxFit.cover,
image: images![index].fileName!.isNotEmpty
? Image.asset(images![index].fileName!).image
: Utility.imageFromBase64String(
images![index].byteString!) // THIS
.image),
)
I would also double check your Utility.imageFromBase64String() function to make sure that works by itself.

To have a separate field for the imageAsset and also do a check for nulls instead of "isNotEmpty" check seems to have fixed the issue. I have no idéa though if this is a good solution, probably not but it works! I will edit the code to reflect the working code. Thank you Banjoe for the suggestion with separate fields!

Related

How to read a data from SQFLite in flutter

i'm using sqflite package and i want to read a data from it ,the data ins in a table namedmy_tablein the table i want to read name property on it and use it in a widget like text widget here is what i've tried \
list = await db.query('my_table', 'name');
#override
Widget build(BuildContext context) {
ScrollController _scrollController = new ScrollController();
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
},
),
body: Container(
color: Colors.blue,
height: 1000,
width: MediaQuery.of(context).size.width,
child: Text('${list}'),
), );
}
Please provide more information!
When you are using SQFLite:
Reading:
https://github.com/tekartik/sqflite/blob/master/sqflite/doc/sql.md
query is for reading a table content. It returns a list of map.
var list = await db.query('my_table', columns: ['name', 'type']);
I can recommend this guide for SQFLite

Flutter rebuild widget with File changed possible?

I have a small Thumbnail widget that takes a File and displays it:
class ThumbnailViewer extends StatelessWidget {
ThumbnailViewer({this.key, this.image}) : super(key: key);
Key key;
File image;
#override
Widget build(BuildContext context) {
return getThumbnail();
}
Widget getThumbnail() {
return ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Center(
child: Image.file(
image,
fit: BoxFit.fitWidth,
width: double.infinity,
filterQuality: FilterQuality.none,
),
),
);
}
}
This widget is first created like this:
return ThumbnailViewer(image: File(FileHelper.getPhotoPath(shroom.photo, shroom.id)));
Then the file that is referenced by that path is changed and setState is called. However the new image is not displayed.
Does flutter not notice file changes when rebuilding? Is there any way to listen to file changes?
Thanks

Saving multiple lines of text in Firestore with Flutter

I am making a note keeping page for my app which has the ability to edit a note title and the note itself.
While using Flutter and Firestore I ran into this problem:
I can't find any way to create something like a TextField which would support multiple lines of text and I don't know how I could store that data in Firestore.
The edit note page code, currently only reading from Firestore:
class EditNotePage extends StatefulWidget {
final DocumentSnapshot document;
EditNotePage({
Key key,
this.document
}) : super(key: key);
#override
_EditNotePageState createState() => _EditNotePageState(document);
}
class _EditNotePageState extends State<EditNotePage> {
final DocumentSnapshot document;
_EditNotePageState(this.document);
#override
Widget build(BuildContext context) {
var textFieldTitleController = new TextEditingController(
text: document['title']
);
var textFieldNoteController = new TextEditingController(
text: document['note']
);
return Scaffold(
appBar: AppBar(
title: Text('Edit note'),
),
body: Column(
children: <Widget>[
TextField(
controller: textFieldTitleController,
decoration: InputDecoration(
labelText: 'Title'
),
),
TextField(
controller: textFieldNoteController,
decoration: InputDecoration(
labelText: 'Note'
),
),
],
),
);
}
}
Firestore structure:
users > "userUID" > notes > "note" > title (as string) and note (as
string)
This would be especially important when making a text editing application or saving long texts written by the user.
Any ideas for solving this issue?
Finally found a solution, Flutter's text field has a maxLines parameter, feed it with null or the number of lines you want and it will seamlessly be stored in Firebase with no formatting problems whatsoever.

Checkbox List Not Updating [duplicate]

I'm trying to get familiar with flutter and I'm facing some weird case. I want to build a dynamic ListView where a + button allows to add elements. I wrote the following State code:
class MyWidgetListState extends State<MyWidgetList> {
List<Widget> _objectList = <Widget>[
new Text('test'),
new Text('test')
];
void _addOne() {
setState(() {
_objectList.add(new Text('test'));
});
}
void _removeOne() {
setState(() {
_objectList.removeLast();
});
}
#override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new ListView(
shrinkWrap: true,
children: _objectList
),
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new IconButton(
icon: new Icon(Icons.remove_circle),
iconSize: 36.0,
tooltip: 'Remove',
onPressed: _objectList.length > 2 ? _removeOne : null,
),
new IconButton(
icon: new Icon(Icons.add_circle),
iconSize: 36.0,
tooltip: 'Add',
onPressed: _addOne,
)
],
),
new Text(_objectList.length.toString())
],
);
}
}
My problem here is that the ListView is visually stuck with the 2 elements I initialized it with.
Internally the _objectList is well managed. For testing purpose I added a simple Text widget at the bottom that shows the size of the list. This one works fine when I click the Add/Remove buttons and it gets properly refreshed. Am I missing something?
Flutter is based around immutable data. Meaning that if the reference to an object didn't change, the content didn't either.
The problem is, in your case you always send to ListView the same array, and instead mutate its content. But this leads to ListView assuming the list didn't change and therefore prevent useless render.
You can change your setState to keep that in mind :
setState(() {
_objectList = List.from(_objectList)
..add(Text("foo"));
});
Another Solution!!
Replace ListView with ListView.builder
Code:
ListView.builder(
itemBuilder: (ctx, item) {
return _objectList[item];
},
shrinkWrap: true,
itemCount: _objectList.length,
),
Output:

Flutter Tabs is throwing a null pointer exception -> Material.of(context).color evaluates to null

I am following the example expansiona_panels_demo.dart provided in the flutter gallery found here. My objective is to introduce a TabBar and TabBarView within the expansion panel body. As of now this is breaking because a null pointer exception being thrown within tabs.dart. This is within _TabBarState and it's doing a comparison against the widget.indicatorColor If you were to look at the demo I am trying to call new BookTabs() at line 251.
if (color.value == Material.of(context).color.value)
Here Material.of(context).color is null
To consolidate the code I have put my tabs in a stateful widget. When I place the widget within a CollapsibleBody widget as defined in the gallery example then the *getter 'value' called on null * is thrown. Currently I am at a loss as to how to resolve this. Any suggestions would be greatly appreciated. Thank you!!
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class BookTabs extends StatefulWidget {
#override
_BookTabs createState() => new _BookTabs();
}
class _BookTabs extends State<BookTabs> with
SingleTickerProviderStateMixin {
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = new TabController(vsync: this, length: 2);
}
#override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new TabBar(
controller: _tabController,
indicatorColor: Colors.blue,
labelStyle: new TextStyle(color: Colors.blue),
labelColor: Colors.blue,
unselectedLabelColor: Colors.indigo,
tabs: [
new Tab(text: 'Books'),
new Tab(text: 'Authors')
]
),
new Expanded(
child: new TabBarView(
controller: _tabController,
children: [
new SafeArea(
top: false,
bottom: false,
child:
new Text("Books", style: Theme
.of(context)
.textTheme
.subhead)),
new SafeArea(
top: false,
bottom: false,
child: new Text("Authors", style: Theme
.of(context)
.textTheme
.subhead))
])
)
],
);
}
}
From Flutter Doctor:
[✓] Flutter (Channel dev, v0.2.6, on Mac OS X 10.13.3 17D102, locale en-US)
• Flutter version 0.2.6 at /Users/minime/flutter
• Framework revision 1d067220da (4 days ago), 2018-03-29 22:14:04 -0700
• Engine revision 9af82c5a1a
• Dart version 2.0.0-dev.43.0.flutter-e305117519
I think you don't use MaterialApp as parent of BookTabs.
If you don't want a MaterialApp, you at least need to wrap it in Material
I experienced a similar problem using Scaffold.of(context) and managed to solve it by wrapping all my widgets under a Builder wrapper.
Check the BuildContext docs on how to do it, when you need a valid context it's the way to go.
Wrapping the widget in a Material class fixed the issue.
new Material(color: Theme
.of(context)
.dialogBackgroundColor, child: new BookTabs()
)

Resources