Cloud firestore documentation for flutter - flutter

I am trying to write an app with flutter using cloud firestore but in the examples page I do not see a flutter/dart option for the example code, am I missing something?
Here is where I am looking at https://firebase.google.com/docs/firestore/query-data/get-data
Any help would be great. Thanks

There are indeed no examples for Flutter in the Firebase documentation. What I do is that I read the Firebase documentation on the topic I'm trying to learn more about, and then use the FlutterFire documentation for Firestore to construct the corresponding Flutter example myself.
To navigate to the Flutter example, but step-wise it (currently) is:
Go to the home page of the cloud_firestore plugin.
Click on the Example tab
I also frequently use the FlutterFire reference documentation for Firestore to look up API signatures for Flutter, based on the examples in the Firebase documentation for Firestore.

Sorry for late answer,
I was resolving my own project issue.
By the way I have Implemented CRUD Operation with cloud_firestore plugin.
SEE_CRUD_OPREATION_OUTPUT_VIDEO
Here you can analyse my
full code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class CRUDoperation extends StatefulWidget {
#override
_CRUDoperationState createState() => _CRUDoperationState();
}
class _CRUDoperationState extends State<CRUDoperation> {
Firestore firestore = Firestore.instance;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("CRUD"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.group_add),
onPressed: (){
showDialog(
context: context,
child: ShowCustomDialogBox(oprationName: "Add",)
);
}
)
],
),
body: Container(
padding: const EdgeInsets.all(10),
alignment: Alignment.center,
child: StreamBuilder<QuerySnapshot>(
stream: firestore.collection('Employee').snapshots(),
builder: (BuildContext context,AsyncSnapshot<QuerySnapshot> snapshot){
if (snapshot.hasError){
return new Center(
child:Text('Error: ${snapshot.error}')
);
}
if(!snapshot.hasData){
return new Center(
child:CircularProgressIndicator()
);
}
else{
var documents = snapshot.data.documents;
if(documents.length>0){
return ListView.builder(
itemCount:documents.length ,
itemBuilder: (context, index){
return Card(
child: ListTile(
leading: IconButton(
icon: Icon(Icons.edit,color: Colors.blue,),
onPressed: (){
showDialog(
context: context,
child: ShowCustomDialogBox(
documentSnapshot:documents[index],
oprationName: "Edit",
)
);
}
),
title: Text(documents[index].data['Name']),
subtitle: Text(documents[index].data['Post']),
trailing: IconButton(
icon: Icon(Icons.delete,color: Colors.red,),
onPressed: (){
firestore.collection('Employee').document(documents[index].documentID)
.delete().then((onValue){ //delete user
print("Deleted successfully");
});
}
),
),
);
}
);
}else{
return Center(
child: Text("Add Emlopyee list"),
);
}
}
}
),
),
);
}
}
//ADD OR EDIT USER DIALOG BOX
class ShowCustomDialogBox extends StatefulWidget {
final DocumentSnapshot documentSnapshot;
final String oprationName;
ShowCustomDialogBox({ this.documentSnapshot, this.oprationName});
#override
State<StatefulWidget> createState() => ShowCustomDialogBoxState();
}
class ShowCustomDialogBoxState extends State<ShowCustomDialogBox>with SingleTickerProviderStateMixin {
TextEditingController nameController;
TextEditingController postController ;
Firestore firestore = Firestore.instance;
#override
void initState() {
super.initState();
nameController = widget.oprationName == "Edit" ? TextEditingController(text: widget.documentSnapshot.data['Name'])
: TextEditingController();
postController = widget.oprationName == "Edit"? TextEditingController(text:widget.documentSnapshot.data['Post'])
: TextEditingController();
}
launchOpration(){
if(widget.oprationName == "Edit"){
editEmployee();
}else{
addEmployee();
}
}
addEmployee(){ //Create user
if(nameController.text.isNotEmpty && postController.text.isNotEmpty){
firestore.collection("Employee").add({
'Name':nameController.text,
'Post':postController.text
})
.then((doc){
print("employee added successfully documentID :${doc.documentID}");
nameController.clear();
postController.clear();
Navigator.of(context).pop();
});
}
else{
print("Please all fields");
}
}
editEmployee(){ //Update User
firestore.collection('Employee').document(widget.documentSnapshot.documentID).updateData({
'Name':nameController.text,
'Post':postController.text
}).then((onValue){
print("employee Edited successfully");
nameController.clear();
postController.clear();
Navigator.of(context).pop();
});
}
#override
void dispose() {
nameController.dispose();
postController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Center(
child: Material(
color: Colors.transparent,
child: Container(
margin: EdgeInsets.all(20.0),
padding: EdgeInsets.all(8.0),
height: MediaQuery.of(context).size.height/2.5,
width: MediaQuery.of(context).size.width,
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0))),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("${widget.oprationName} Employee"),
SizedBox(height:10),
TextField(
controller: nameController,
decoration: InputDecoration(
hintText: "Enter Name",
border: OutlineInputBorder()
),
),
SizedBox(height:10),
TextField(
controller: postController,
decoration: InputDecoration(
hintText: "Enter Post",
border: OutlineInputBorder()
),
),
],
),
),
Padding(
padding: const EdgeInsets.only(
left: 20.0, right: 10.0, top: 0.0,),
child: ButtonTheme(
height: 35.0,
minWidth: MediaQuery.of(context).size.width/3.5,
child: RaisedButton(
color: Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0)),
splashColor: Colors.white.withAlpha(40),
child: Text(
widget.oprationName,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 13.0),
),
onPressed: () {
launchOpration();
},
)
)
),
],
)
),
),
);
}
}

Related

flutter transfer data (color) to create a new widget

I'm creating a calendar app. The problem that I'm now facing is that I want to create a new user of the calendar. The user has the properties (which are now important) image, name and color.
I created a new File For the property color, in which the color can be changed. But I don't know how I can transfer the new color in the other file, so that I can use it to create the user.
I think it is possible to use the Material page route, but perhaps there is a more elegant way to handle this.
Does someone have an idea to handle this in a easy way?
UserSetScreen:
import 'package:calendar_vertical/screens/users_show_screen.dart';
import 'package:calendar_vertical/widgets/color_choose.dart';
import 'package:calendar_vertical/widgets/image_input.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class UserSetScreen extends StatefulWidget {
static const routeName = '/userSetScreen';
#override
State<UserSetScreen> createState() => _UserSetScreenState();
}
class _UserSetScreenState extends State<UserSetScreen> {
final _titleController = TextEditingController();
static const values = <String>[
'Administrator',
'normaler Nutzer',
'eingeschränkter Nutzer'
];
String selectedValue = values.first;
void _saveValues(User user) {
final neuerNutzer = User(
id: DateTime.now().toString(),
name: _titleController.text,
color: Colors.amber,
setAppointments: false,
administrator: false,
);
}
#override
Widget build(BuildContext context) {
final colorData = Provider.of<ColorChoose>(context);
return Scaffold(
appBar: AppBar(
title: Text('Person hinzufügen'),
actions: [
IconButton(
onPressed: () {
Navigator.of(context).pushNamed(UsersShowScreen.routeName);
},
icon: Icon(Icons.people),
),
],
),
body: Column(
children: [
Center(
child: ImageInput(),
),
Expanded(
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(10),
child: Column(
children: [
TextField(
decoration: InputDecoration(labelText: 'Name'),
controller: _titleController,
),
ColorChoose(),
//CheckboxListTile(
// value: value,
// onChanged: (value) => setState(() => this.value = value!),
// title: Text('Administrator'),
// controlAffinity: ListTileControlAffinity.leading,
//)
],
),
),
))
],
),
);
}
ColorChoose:
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
class ColorChoose extends StatefulWidget {
#override
State<ColorChoose> createState() => _ColorChooseState();
}
class _ColorChooseState extends State<ColorChoose> {
Color currentColor = Colors.white;
#override
Widget build(BuildContext context) {
return Row(
children: [
Text('Farbe: '),
Container(
decoration: BoxDecoration(
color: currentColor,
borderRadius: BorderRadius.all(
Radius.circular(15),
),
),
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),
margin: EdgeInsets.only(left: 10.0),
),
Spacer(),
ElevatedButton(
onPressed: () => _showColorPicker(context),
child: Text(
'Farbe ändern',
),
),
],
);
}
void _showColorPicker(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Farbe wählen'),
titlePadding: const EdgeInsets.all(0.0),
contentPadding: const EdgeInsets.all(0.0),
content: SingleChildScrollView(
child: Wrap(
children: [
Container(
width: 300,
height: 300,
child: BlockPicker(
pickerColor: currentColor,
onColorChanged: (color) => setState(
() => this.currentColor = color,
),
),
)
],
),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('Close'),
)
],
),
);
}
}
Thank you very much.
Best regards
Patrick
I guess the best variant is to use GetX or another state manager.
Another way - to choose color right from the user screen, showing a dialog.
Finally you can pass valuenotifier to your color ColorChoose widget.

how to make flutter searchDelagate a separate screen that can be navigated to independently?

i have a page called searchUsersSCreen which is this:
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:myApp/models/otherUser.dart';
import 'package:myApp/ui/widgets/user_profile.dart';
import 'database.dart';
class SearchUsersScreen extends StatefulWidget {
#override
_SearchUsersScreenState createState() => _SearchUsersScreenState();
}
class _SearchUsersScreenState extends State<SearchUsersScreen> {
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => showSearch(
context: context,
delegate: SearchUsers(
DatabaseService().fetchUsersInSearch(),
),
));
}
#override
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).primaryColor,
);
}
}
and inside the same dart file is have this searchDelegate :
//Search delegate
class SearchUsers extends SearchDelegate<OtherUser> {
final Stream<QuerySnapshot> otherUser;
final String hashtagSymbol = 'assets/svgs/flaticon/hashtag_symbol.svg';
SearchUsers(this.otherUser);
#override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
icon: Icon(Icons.clear),
onPressed: () {
query = '';
},
),
];
}
#override
Widget buildLeading(BuildContext context) {
return Container(
width: 0,
height: 0,
);
}
#override
Widget buildResults(BuildContext context) {
return Container(
width: 0,
height: 0,
color: Theme.of(context).primaryColor,
);
}
#override
Widget buildSuggestions(BuildContext context) {
showUserProfile(String userId) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserProfileView(
userUid: userId,
)));
}
return StreamBuilder<QuerySnapshot>(
stream: DatabaseService().fetchUsersInSearch(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
final handlesResults = snapshot.data.documents
.where((u) => u['username'].contains(query));
if (!snapshot.hasData) {
return Container(
color: Theme.of(context).primaryColor,
child: Center(
child: Text(
'',
style: TextStyle(
fontSize: 16, color: Theme.of(context).primaryColor),
),
),
);
}
if (handlesResults.length > 0) {
return Container(
color: Theme.of(context).primaryColor,
child: ListView(
children: handlesResults
.map<Widget>((u) => GestureDetector(
child: Padding(
padding: const EdgeInsets.all(0.1),
child: Container(
padding: EdgeInsets.symmetric(vertical: 5),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
border: Border(
bottom: BorderSide(
width: 0.3, color: Colors.grey[50]))),
child: ListTile(
leading: CircleAvatar(
backgroundColor:
Theme.of(context).primaryColor,
backgroundImage:
NetworkImage(u['userAvatarUrl']),
radius: 20,
),
title: Container(
padding: EdgeInsets.only(left: 10),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(u['username'],
style: TextStyle(
fontSize: 16,
color: Theme.of(context)
.accentColor),
overflow: TextOverflow.ellipsis),
SizedBox(
height: 5,
),
Text(u['name'],
style: TextStyle(
fontSize: 16,
color: Colors.grey[500],
),
overflow: TextOverflow.ellipsis),
],
),
),
trailing: Container(
padding: EdgeInsets.only(left: 10),
height: 43.0,
width: MediaQuery.of(context).size.width / 2,
),
),
),
),
onTap: () {
showUserProfile(u['id']);
},
))
.toList(),
),
);
} else {
return Container(
color: Theme.of(context).primaryColor,
child: Center(
child: Text(
'No results found',
style: TextStyle(
fontSize: 16,
color: Theme.of(context).accentColor,
),
),
),
);
}
});
}
}
i wanted to user the class SearchUsers as a separate screen that i can navigate to independently...but couldn't achieve that as SearchUsers doesn't evaluate to a widget.
so i built SearchUsersScreen statefulWidget and inside it's initState() i called this:
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => showSearch(
context: context,
delegate: SearchUsers(
DatabaseService().fetchUsersInSearch(),
),
));
}
as to make the search feature starts automatically when the user navigates to SearchUsersScreen.
and i ended up into two problems:
SearchUsers is being displayed in full screen ontop of SearchUsersSCreen (which i don't want this behavior), i want it to be displayed inside of it.
actually its covering the BottomNavigationBar i built for navigation between screens.
after SearchUsers is being displayed (and its doing its job well), when i tap the device back button...i leave SearchUsers and get back to SearchUsersScreen....which is indeed a blank screen.
so to wrap it up...all i want is to use SearchUsers class as a widget that i can navigate to and navigate from independently...thats it.
any help would be much appreciated.
thanks for your time reading.
Instead of trying to create a separate widget SearchUsers, try to create a dialog and show it when anyone wants to search users. You can also use the navigator and the back button in this case and get arguments passed from the next screen to the previous screen.

Flutter - The method '|' was called on null. After hot reload it is working

I am pretty new in flutter. I don't know what happening in background because after hot reload its work fine. On another dart files that happens, firebase dont provide me data on initialization just after hot reload.
class CityServices {
getCites() {
return Firestore.instance.collection('cities').getDocuments();
}
}
class _HomeScreenState extends State<HomeScreen> {
bool citiesFlag = false;
var cities;
int citiesCount;
String actualCity;
Maybe mistake is here.
#override
void initState() {
super.initState();
CityServices().getCites().then((QuerySnapshot) {
if (QuerySnapshot.documents.isNotEmpty) {
citiesFlag = true;
cities = QuerySnapshot.documents;
citiesCount = QuerySnapshot.documents.length;
}
});
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: MyColors.vintageGreen,
appBar: AppBar(
backgroundColor: MyColors.background,
title: Center(
child: Text(
'Válasszon települést...',
style: GoogleFonts.barlowCondensed(
color: MyColors.appbarText,
fontSize: 26.0,
fontWeight: FontWeight.w500),
),
),
),
body: Center(
child: Container(
child: GridView.count(
crossAxisCount: 2,
children: List.generate(citiesCount, (index) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: InkWell(
onTap: () {
actualCity = cities[index]['city_name'];
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
CityView(cityName: actualCity)),
);
},
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
title: Center(
child: Text(
cities[index]['city_name'],
style: TextStyle(
fontWeight: FontWeight.w500, fontSize: 18.0),
)),
subtitle: Center(child: Text('22 bejegyzés')),
),
Flexible(
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(5)),
child: Padding(
padding: EdgeInsets.only(bottom: 15.0),
child: Image(
image: AssetImage(
cities[index]['img_path'],
),
),
),
),
)
],
),
),
color: MyColors.background,
);
}),
),
),
),
),
);
}
}
Maybe here is the mistake? Should it be on top of dart file?
class HomeScreen extends StatefulWidget {
static const String id = 'home';
#override
_HomeScreenState createState() => new _HomeScreenState();
}
Let me explain the issue and why it is happening, then propose few solutions.
inside initState you are calling CityServices().getCites().then... which is an async method.
However, when your widget is built for the first time, the data you expect from Firestore is not ready yet, thus you get null for both cities and citiesCount.
Short term solution:
make sure there is null check, display indicator while waiting for the data.
body: Center(
child: (cities == null) ?
CircularProgressIndicator()
: Container(...
Additionally, you can also refactor your initState to something like this
void getCities() async {
var snapshot CityServices().getCites();
setState(() {
citiesFlag = true;
cities = snapshot.documents;
citiesCount = snapshot.documents.length;
});
}
#override
void initState() {
getCities();
super.initState();
}
Long term solution:
use BLoC pattern and make data loading decoupled from UI.
see flutter_bloc for how to implement it.

Passing value to previous widget

I have simple form , inside it have CircularAvatar when this is pressed show ModalBottomSheet to choose between take picture from gallery or camera. To make my widget more compact , i separated it to some file.
FormDosenScreen (It's main screen)
DosenImagePicker (It's only CircularAvatar)
ModalBottomSheetPickImage (It's to show ModalBottomSheet)
The problem is , i don't know how to passing value from ModalBottomSheetPickImage to FormDosenScreen. Because value from ModalBottomSheetPickImage i will use to insert operation.
I only success passing from third Widget to second Widget , but when i passing again from second Widget to first widget the value is null, and i think the problem is passing from Second widget to first widget.
How can i passing from third Widget to first Widget ?
First Widget
class FormDosenScreen extends StatefulWidget {
static const routeNamed = '/formdosen-screen';
#override
_FormDosenScreenState createState() => _FormDosenScreenState();
}
class _FormDosenScreenState extends State<FormDosenScreen> {
String selectedFile;
#override
Widget build(BuildContext context) {
final detectKeyboardOpen = MediaQuery.of(context).viewInsets.bottom;
print('trigger');
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Tambah Dosen'),
actions: <Widget>[
PopupMenuButton(
itemBuilder: (_) => [
PopupMenuItem(
child: Text('Tambah Pelajaran'),
value: 'add_pelajaran',
),
],
onSelected: (String value) {
switch (value) {
case 'add_pelajaran':
Navigator.of(context).pushNamed(FormPelajaranScreen.routeNamed);
break;
default:
}
},
)
],
),
body: Stack(
fit: StackFit.expand,
children: <Widget>[
SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
SizedBox(height: 20),
DosenImagePicker(onPickedImage: (file) => selectedFile = file),
SizedBox(height: 20),
Card(
margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextFormFieldCustom(
onSaved: (value) {},
labelText: 'Nama Dosen',
),
SizedBox(height: 20),
TextFormFieldCustom(
onSaved: (value) {},
prefixIcon: Icon(Icons.email),
labelText: 'Email Dosen',
keyboardType: TextInputType.emailAddress,
),
SizedBox(height: 20),
TextFormFieldCustom(
onSaved: (value) {},
keyboardType: TextInputType.number,
inputFormatter: [
// InputNumberFormat(),
WhitelistingTextInputFormatter.digitsOnly
],
prefixIcon: Icon(Icons.local_phone),
labelText: 'Telepon Dosen',
),
],
),
),
),
SizedBox(height: kToolbarHeight),
],
),
),
Positioned(
child: Visibility(
visible: detectKeyboardOpen > 0 ? false : true,
child: RaisedButton(
onPressed: () {
print(selectedFile);
},
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
color: colorPallete.primaryColor,
child: Text(
'SIMPAN',
style: TextStyle(fontWeight: FontWeight.bold, fontFamily: AppConfig.headerFont),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
textTheme: ButtonTextTheme.primary,
),
),
bottom: kToolbarHeight / 2,
left: sizes.width(context) / 15,
right: sizes.width(context) / 15,
)
],
),
);
}
}
Second Widget
class DosenImagePicker extends StatefulWidget {
final Function(String file) onPickedImage;
DosenImagePicker({#required this.onPickedImage});
#override
DosenImagePickerState createState() => DosenImagePickerState();
}
class DosenImagePickerState extends State<DosenImagePicker> {
String selectedImage;
#override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.center,
child: InkWell(
onTap: () async {
await showModalBottomSheet(
context: context,
builder: (context) => ModalBottomSheetPickImage(
onPickedImage: (file) {
setState(() {
selectedImage = file;
widget.onPickedImage(selectedImage);
print('Hellooo dosen image picker $selectedImage');
});
},
),
);
},
child: CircleAvatar(
foregroundColor: colorPallete.black,
backgroundImage: selectedImage == null ? null : MemoryImage(base64.decode(selectedImage)),
radius: sizes.width(context) / 6,
backgroundColor: colorPallete.accentColor,
child: selectedImage == null ? Text('Pilih Gambar') : SizedBox(),
),
),
);
}
}
Third Widget
class ModalBottomSheetPickImage extends StatelessWidget {
final Function(String file) onPickedImage;
ModalBottomSheetPickImage({#required this.onPickedImage});
#override
Widget build(BuildContext context) {
return SizedBox(
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Wrap(
alignment: WrapAlignment.spaceEvenly,
children: <Widget>[
InkWell(
onTap: () async {
final String resultBase64 =
await commonFunction.pickImage(quality: 80, returnFile: ReturnFile.BASE64);
onPickedImage(resultBase64);
},
child: CircleAvatar(
foregroundColor: colorPallete.white,
backgroundColor: colorPallete.green,
child: Icon(Icons.camera_alt),
),
),
InkWell(
onTap: () async {
final String resultBase64 =
await commonFunction.pickImage(returnFile: ReturnFile.BASE64, isCamera: false);
onPickedImage(resultBase64);
},
child: CircleAvatar(
foregroundColor: colorPallete.white,
backgroundColor: colorPallete.blue,
child: Icon(Icons.photo_library),
),
),
],
),
),
);
}
}
The cleanest and easiest way to do this is through Provider. It is one of the state management solutions you can use to pass values around the app as well as rebuild only the widgets that changed. (Ex: When the value of the Text widget changes). Here is how you can use Provider in your scenario:
This is how your model should look like:
class ImageModel extends ChangeNotifier {
String _base64Image;
get base64Image => _base64Image;
set base64Image(String base64Image) {
_base64Image = base64Image;
notifyListeners();
}
}
Don't forget to add getters and setters so that you can use notifyListeners() if you have any ui that depends on it.
Here is how you can access the values of ImageModel in your UI:
final model=Provider.of<ImageModel>(context,listen:false);
String image=model.base64Image; //get data
model.base64Image=resultBase64; //set your image data after you used ImagePicker
Here is how you can display your data in a Text Widget (Ideally, you should use Selector instead of Consumer so that the widget only rebuilds if the value its listening to changes):
#override
Widget build(BuildContext context) {
//other widgets
Selector<ImageModel, String>(
selector: (_, model) => model.base64Image,
builder: (_, image, __) {
return Text(image);
},
);
}
)
}
You could achieve this easily. If you are using Blocs.

How to display a single property of an object in listview

I have a list of goal objects with two properties, description (what I want to display) and ID (used as a key to identify it). Ultimately I want a list of goal descriptions (ex. mow lawn, get groceries etc) but I'm confused how to specify a single property with the listview builder. The reason I'm using an object is because I want to use swipe to dismiss on the list. I'm using an object to give each goal a unique key, therefore when I swipe to dismiss I can safely undo the dismissal / reorder the list.
File Structure: lib folder contains functions, goals and main. A sub-folder in the lib folder called UI contains form and home.
main.dart
import 'package:flutter/material.dart';
import 'package:aurelius/UI/home.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context){
return new MaterialApp(
debugShowCheckedModeBanner: false,
title: "ToDo",
home: myWidgets(),
);
}
}
Widget myWidgets(){
return GoalsList();
}
home.dart
import 'package:flutter/material.dart';
import 'package:aurelius/goals.dart';
import 'package:aurelius/functions.dart';
//Goals List Variables
var goals = List<Goals>();
final TextEditingController listCtrl = new TextEditingController();
class GoalsList extends StatefulWidget{
#override
_GoalsListState createState() => _GoalsListState();
}
class _GoalsListState extends State<GoalsList>{
final formKey = GlobalKey<FormState>(); //key for goal form
#override
Widget build(BuildContext context){
final listSize = MediaQuery.of(context).size.height * 1;
return Scaffold(
resizeToAvoidBottomPadding: false,
extendBody: true,
backgroundColor: Colors.black,
//Navigation Bar
floatingActionButton: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.white,
),
borderRadius: BorderRadius.circular(25.0),
),
child: FloatingActionButton.extended(
elevation: 4.0,
icon: const Icon(Icons.add),
label: const Text('Add Goal'),
backgroundColor: Colors.black,
splashColor: Colors.white,
//Pop-up Dialogue
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context){
return AlertDialog(
title: Center(child: new Text("New Goal:",)),
content: Form(
key: formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextFormField(
decoration: InputDecoration(
border: OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(12))),
),
controller: listCtrl,
),
RaisedButton(
child: Text("ADD"),
onPressed: (){
goals.add(createGoal(listCtrl.text));
listCtrl.clear();
Navigator.pop(context);
},
splashColor: Colors.blue,
elevation: 2,
)
]
),
)
);
}
);
},
),
),
),
floatingActionButtonLocation:FloatingActionButtonLocation.centerDocked,
//Bottom App Bar
bottomNavigationBar: BottomAppBar(
shape: CircularNotchedRectangle(),
notchMargin: -30.0,
color: Colors.black,
child: new Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(icon: Icon(Icons.person_outline),color: Colors.white,splashColor: Colors.white, onPressed: (){},),
IconButton(icon: Icon(Icons.settings),color: Colors.white,splashColor: Colors.white, onPressed: (){},),
],
),
),
//Goals List Box
body: Column(children: <Widget>[
SizedBox(height: listSize,
child: ListView.builder(
itemCount: goals.length,
itemBuilder: (context,index){
return Dismissible(
key: UniqueKey(),
//Green background and icon for left side swipe
background: Container(
color: Colors.green[300],
padding: EdgeInsets.symmetric(horizontal: 20),
alignment: AlignmentDirectional.centerStart,
child: Icon(
Icons.check_box,
color: Colors.white,
),
),
//Green background and icon for right side swipe
secondaryBackground: Container(
color: Colors.green[300],
padding: EdgeInsets.symmetric(horizontal: 20),
alignment: AlignmentDirectional.centerEnd,
child: Icon(
Icons.check_box,
color: Colors.white,
),
),
onDismissed:(direction){
if(goals.contains(index)){
setState((){
goals.removeAt(index);
});
}
},
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(goals[index].description),
],
),
),
);
},
),
),
//Potential more rows here
],
)
);
}
}
}
goals.dart
import 'package:flutter/material.dart';
import 'package:aurelius/UI/home.dart';
class Goals{
String description; //part visible to user
int id;
Goals({this.description,this.id});
}
functions.dart
import 'package:flutter/material.dart';
import 'package:aurelius/goals.dart';
import 'package:uuid/uuid.dart';
createGoal(String text){
var goal = new Goals();
goal.description = text;
goal.id = new DateTime.now().millisecondsSinceEpoch;
return goal;
}
form.dart
import 'package:flutter/material.dart';
class AddButton extends StatefulWidget {
#override
AddButtonState createState() => new AddButtonState();
}
class AddButtonState extends State<AddButton>{
Color addbuttoncolor = Colors.red;
IconData addIcon = Icons.add;
void onPressed(){
setState((){
if (addIcon == Icons.add) {
addIcon = Icons.clear;
}
else{
addIcon = Icons.add;
}
});
}
#override
Widget build(BuildContext context){
return Scaffold(
body: Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new RawMaterialButton(
onPressed: onPressed,
child: new Icon(
addIcon,
color: Colors.blue,
size: 35.0,
),
shape: new CircleBorder(),
elevation: 2.0,
fillColor: Colors.white,
padding: const EdgeInsets.all(15.0),
),
]
),
),
)
);
}
}
I think the problem is when you creating the goal, you don't return created goal. Your createGoal() method should return the goal like below:
createGoal(String text){
var goal = new Goals();
goal.description = text;
goal.id = new DateTime.now().millisecondsSinceEpoch;
return goal; // Add this
}
The only issue I see is that you don't return the object you create in createGoal().
To display the description with the Dismissible, you would just set its child to child: Text(goals[index].description)
To optimize the code, you can initialize the Goal id directly in the class itself. You can set the constructor as Goals(this.description) and the createGoal function will not be needed anymore.
PS: Keeping your class names singular is a better practice