Related
Hi Im trying to use List views to make the UI more interactive and appealing, however I cant figure out a way to pass the TextEditingController() pass through to other pages. I've attached the relevant pages of code below.
admin_exercise.dart
this creates both list views this is then passed to admin_screen.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
final _firestore = Firestore.instance;
late FirebaseUser users;
class ExerciseView extends StatefulWidget {
const ExerciseView({Key? key}) : super(key: key);
#override
State<ExerciseView> createState() => _ExerciseViewState();
}
class _ExerciseViewState extends State<ExerciseView> {
void getUsers() {
users.email.toString();
}
var selectedUser;
bool setDefaultUser = true;
var selectedExercise;
bool setDefaultExercise = true;
int set1 = 0;
List<Widget> _cardList = [];
void _addCardWidget() {
setState(() {
_cardList.add(SetCard(
setNo: (set1 + 1),
exercise: selectedExercise,
));
});
}
void _deleteCardWidget() {
setState(() {
_cardList.removeLast();
});
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Center(
child: StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('exercises')
.orderBy('Title')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return Container();
if (setDefaultExercise) {
selectedExercise = snapshot.data?.documents[0]['Title'];
}
return DropdownButton(
isExpanded: false,
value: selectedExercise,
items: snapshot.data?.documents.map((value2) {
return DropdownMenuItem(
value: value2['Title'],
child: Text('${value2['Title']}'),
);
}).toList(),
onChanged: (value2) {
setState(
() {
// Selected value will be stored
selectedExercise = value2;
// Default dropdown value won't be displayed anymore
setDefaultExercise = false;
},
);
},
);
},
),
),
ElevatedButton(
onPressed: () {
setState(() {
_addCardWidget();
set1++;
});
},
child: Icon(Icons.add)),
Text('Sets: $set1'),
ElevatedButton(
onPressed: () {
_deleteCardWidget();
setState(() {
set1--;
});
},
child: Icon(Icons.remove))
],
),
ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: _cardList.length,
itemBuilder: (context, index) {
return _cardList[index];
}),
],
);
}
}
class SetCard extends StatelessWidget {
SetCard({required this.setNo, required this.exercise});
late int setNo;
late String exercise;
final repTextController = TextEditingController();
final notesTextController = TextEditingController();
Future<void> insertData(final reps) async {
_firestore.collection("plans").add(reps).then((DocumentReference document) {
print(document.documentID);
}).catchError((e) {
print(e);
});
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
// added in exercise here to help to understand how to write to firebase
Text('Set $setNo, $exercise'),
Row(
children: [
Expanded(
flex: 1,
child: TextFormField(
controller: repTextController,
decoration: InputDecoration(hintText: 'Reps...'),
keyboardType: TextInputType.number,
),
),
SizedBox(
width: 10,
),
Expanded(
flex: 4,
child: TextFormField(
controller: notesTextController,
decoration: InputDecoration(hintText: 'Notes...'),
keyboardType: TextInputType.number,
),
),
],
),
],
);
}
}
admin_screen.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:intl/intl.dart';
import 'package:fitness_guide/components/admin_exercise.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class AdminScreen extends StatefulWidget {
static const String id = 'AdminScreen';
const AdminScreen({Key? key}) : super(key: key);
#override
State<AdminScreen> createState() => _AdminScreenState();
}
class _AdminScreenState extends State<AdminScreen> {
TextEditingController dateinput = TextEditingController();
final _auth = FirebaseAuth.instance;
final _firestore = Firestore.instance;
late FirebaseUser users;
void getUsers() {
users.email.toString();
}
var selectedUser;
bool setDefaultUser = true;
var selectedExercise;
bool setDefaultExercise = true;
int set1 = 0;
#override
void initState() {
dateinput.text = ""; //set the initial value of text field
super.initState();
}
List<Widget> _exerciseList = [];
void _addExerciseWidget() {
setState(() {
_exerciseList.add(ExerciseView());
});
}
void _deleteExerciseWidget() {
setState(() {
_exerciseList.removeLast();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
//submit to fbase here
},
label: const Text('Submit'),
icon: const Icon(Icons.thumb_up),
backgroundColor: Color(0xff75D6F2),
),
body: ListView(
physics: BouncingScrollPhysics(),
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(
height: 20,
),
Container(
padding: EdgeInsets.symmetric(horizontal: 15),
height: 50,
child: Center(
child: TextField(
controller:
dateinput, //editing controller of this TextField
decoration: InputDecoration(
icon: Icon(Icons.calendar_today), //icon of text field
labelText: "Enter Date" //label text of field
),
readOnly:
true, //set it true, so that user will not able to edit text
onTap: () async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(
2000), //DateTime.now() - not to allow to choose before today.
lastDate: DateTime(2101));
if (pickedDate != null) {
print(
pickedDate); //pickedDate output format => 2021-03-10 00:00:00.000
String formattedDate =
DateFormat('yyyy-MM-dd').format(pickedDate);
print(
formattedDate); //formatted date output using intl package => 2021-03-16
//you can implement different kind of Date Format here according to your requirement
setState(() {
dateinput.text =
formattedDate; //set output date to TextField value.
});
} else {
print("Date is not selected");
}
},
))),
Center(
child: StreamBuilder<QuerySnapshot>(
stream: _firestore
.collection('users')
.orderBy('email')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
// Safety check to ensure that snapshot contains data
// without this safety check, StreamBuilder dirty state warnings will be thrown
if (!snapshot.hasData) return Container();
// Set this value for default,
// setDefault will change if an item was selected
// First item from the List will be displayed
if (setDefaultUser) {
selectedUser = snapshot.data?.documents[0]['email'];
}
return DropdownButton(
isExpanded: false,
value: selectedUser,
items: snapshot.data?.documents.map((value1) {
return DropdownMenuItem(
value: value1['email'],
child: Text('${value1['email']}'),
);
}).toList(),
onChanged: (value1) {
setState(
() {
// Selected value will be stored
selectedUser = value1;
// Default dropdown value won't be displayed anymore
setDefaultUser = false;
},
);
},
);
},
),
),
//Row below for the first exercise sets etc
ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: _exerciseList.length,
itemBuilder: (context, index) {
return _exerciseList[index];
}),
Center(
child: Text('Add another Exercise...'),
),
ElevatedButton(
onPressed: () {
_addExerciseWidget();
},
child: Icon(Icons.add),
),
],
),
],
),
);
}
}```
p.s. sorry for any spagetti code
[Image shows the layout UI I'm trying to achieve][1]
[1]: https://i.stack.imgur.com/d3qxg.png
I am trying to load more data once I reach the end of the stream builder, everytime I reach the end, the stream builder reloads and bounces back to the beginning, and it does not allow me to scroll down ( keep reload and bounce back to the top).
What I tried is once I reach the end, increase the limit from the firebase, but I am not sure what the problem is.. anyone can help me with this?
Here is the sample code for what I want to do
_onEndScroll(ScrollMetrics metrics) {
//loadToTrue();
setState(() {
documentLimit = documentLimit + 10;
});
}
StreamBuilder(
initialData: cache,
stream: FirebaseFirestore.instance
.collection("timeline")
.doc(widget.currentUser.id)
.collection('timelinePosts')
.orderBy('timestamp', descending: true)
.limit(documentLimit)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> streamSnapshot) {
items = streamSnapshot.data != null &&
streamSnapshot.data.docs != null
? streamSnapshot.data.docs
: [];
List<Post> posts =
items.map((doc) => Post.fromDocument(doc)).toList();
cache = streamSnapshot.data;
return !streamSnapshot.hasData ||
streamSnapshot.connectionState ==
ConnectionState.waiting
? Center(
child: CircularProgressIndicator(),
)
: NotificationListener<ScrollNotification>(
onNotification: (scrollNotification) {
if (scrollNotification is ScrollEndNotification) {
_onEndScroll(scrollNotification.metrics);
}
},
child: Container(
height: MediaQuery.of(context).size.height - 200,
margin: EdgeInsets.only(bottom: 1),
child: ListView.builder(
physics: BouncingScrollPhysics(
parent: ScrollPhysics()),
shrinkWrap: true,
itemCount: items.length,
itemBuilder: (_, i) =>
timelineDecision == "follow"
? posts[i]
: postsLocal[i])));
})
and this is code for all
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:fluttershare/pages/home.dart';
import 'package:fluttershare/models/user.dart';
import 'package:fluttershare/pages/search.dart';
import 'package:fluttershare/pages/upload.dart';
import 'package:fluttershare/pages/upload_limit.dart';
import 'package:fluttershare/widgets/post.dart';
import 'package:latlong/latlong.dart';
final usersRef = FirebaseFirestore.instance.collection('users');
class Timeline extends StatefulWidget {
final User currentUser;
Timeline({this.currentUser});
#override
_TimelineState createState() => _TimelineState();
}
class _TimelineState extends State<Timeline> {
QuerySnapshot cache;
List<Post> posts;
List<Post> postsLocal;
List<DocumentSnapshot> items;
List<String> followingList = [];
String timelineDecision = "local";
String address = "Norman";
ScrollController listScrollController;
int documentLimit = 5;
void initState() {
super.initState();
getFollowing();
}
getFollowing() async {
QuerySnapshot snapshot = await followingRef
.doc(currentUser.id)
.collection('userFollowing')
.get();
if (mounted) {
setState(() {
followingList = snapshot.docs.map((doc) => doc.id).toList();
});
}
}
setTimeline(String timelineDecision) {
setState(() {
this.timelineDecision = timelineDecision;
});
}
buildToggleTimeline() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
TextButton(
onPressed: () => setTimeline("follow"),
child: Text('Following',
style: TextStyle(
color: timelineDecision == 'follow'
? Theme.of(context).primaryColor
: Colors.grey)),
style: TextButton.styleFrom(
textStyle: const TextStyle(fontSize: 20),
),
),
TextButton(
onPressed: () => setTimeline("local"),
child: Text('Local',
style: TextStyle(
color: timelineDecision == 'local'
? Theme.of(context).primaryColor
: Colors.grey)),
style: TextButton.styleFrom(
textStyle: const TextStyle(fontSize: 20),
)),
],
);
}
_onEndScroll(ScrollMetrics metrics) {
//loadToTrue();
setState(() {
documentLimit = documentLimit + 10;
});
}
#override
Widget build(context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: const Text('Entango'),
actions: <Widget>[
PopupMenuButton(
icon: const Icon(Icons.add),
onSelected: choiceAction,
itemBuilder: (context) => [
PopupMenuItem(
child: Text("Timelimit post"),
value: 1,
),
PopupMenuItem(
child: Text("Normal post"),
value: 2,
)
]),
IconButton(
icon: const Icon(Icons.search),
tooltip: 'Show Snackbar',
onPressed: () => Navigator.push(
context, MaterialPageRoute(builder: (context) => Search())),
)
],
),
body: ListView(
children: <Widget>[
buildToggleTimeline(),
Divider(
height: 0.0,
),
//buildSlider(),
StreamBuilder(
initialData: cache,
stream: timelineDecision == "follow"
? FirebaseFirestore.instance
.collection("timeline")
.doc(widget.currentUser.id)
.collection('timelinePosts')
.orderBy('timestamp', descending: true)
.limit(documentLimit)
.snapshots()
: FirebaseFirestore.instance
.collection("postsLocalRef")
.doc(address)
.collection("userPosts")
.orderBy('timestamp', descending: true)
.limit(documentLimit)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> streamSnapshot) {
items = streamSnapshot.data != null &&
streamSnapshot.data.docs != null
? streamSnapshot.data.docs
: [];
List<Post> posts =
items.map((doc) => Post.fromDocument(doc)).toList();
List<Post> postsLocal =
items.map((doc) => Post.fromDocument(doc)).toList();
cache = streamSnapshot.data;
return !streamSnapshot.hasData ||
streamSnapshot.connectionState ==
ConnectionState.waiting
? Center(
child: CircularProgressIndicator(),
)
: NotificationListener<ScrollNotification>(
onNotification: (scrollNotification) {
if (scrollNotification is ScrollEndNotification) {
_onEndScroll(scrollNotification.metrics);
}
},
child: Container(
height: MediaQuery.of(context).size.height - 200,
margin: EdgeInsets.only(bottom: 1),
child: ListView.builder(
physics: BouncingScrollPhysics(
parent: ScrollPhysics()),
shrinkWrap: true,
itemCount: items.length,
itemBuilder: (_, i) =>
timelineDecision == "follow"
? posts[i]
: postsLocal[i])));
})
],
),
);
}
void choiceAction(int value) {
if (value == 1) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Upload_limit(
currentUser: currentUser,
)));
} else {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Upload(
currentUser: currentUser,
)));
}
}
}
There are few issues I could find. One of them is using StreamBuilder to fetch paged data. While it might be great in theory but it won't work in case of Firebase as Firebase is providing Stream. So every-time, setState is called, a new steam will be created. I have wrote sample app for fetching data from firestore in paginated way.
//main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({Key? key, required this.title}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static const PAGE_SIZE = 30;
bool _allFetched = false;
bool _isLoading = false;
List<ColorDetails> _data = [];
DocumentSnapshot? _lastDocument;
#override
void initState() {
super.initState();
_fetchFirebaseData();
}
Future<void> _fetchFirebaseData() async {
if (_isLoading) {
return;
}
setState(() {
_isLoading = true;
});
Query _query = FirebaseFirestore.instance
.collection("sample_data")
.orderBy('color_label');
if (_lastDocument != null) {
_query = _query.startAfterDocument(_lastDocument!).limit(PAGE_SIZE);
} else {
_query = _query.limit(PAGE_SIZE);
}
final List<ColorDetails> pagedData = await _query.get().then((value) {
if (value.docs.isNotEmpty) {
_lastDocument = value.docs.last;
} else {
_lastDocument = null;
}
return value.docs
.map((e) => ColorDetails.fromMap(e.data() as Map<String, dynamic>))
.toList();
});
setState(() {
_data.addAll(pagedData);
if (pagedData.length < PAGE_SIZE) {
_allFetched = true;
}
_isLoading = false;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: NotificationListener<ScrollEndNotification>(
child: ListView.builder(
itemBuilder: (context, index) {
if (index == _data.length) {
return Container(
key: ValueKey('Loader'),
width: double.infinity,
height: 60,
child: Center(
child: CircularProgressIndicator(),
),
);
}
final item = _data[index];
return ListTile(
key: ValueKey(
item,
),
tileColor: Color(item.code | 0xFF000000),
title: Text(
item.label,
style: TextStyle(color: Colors.white),
),
);
},
itemCount: _data.length + (_allFetched ? 0 : 1),
),
onNotification: (scrollEnd) {
if (scrollEnd.metrics.atEdge && scrollEnd.metrics.pixels > 0) {
_fetchFirebaseData();
}
return true;
},
),
);
}
}
class ColorDetails {
final String label;
final int code;
ColorDetails(this.code, this.label);
factory ColorDetails.fromMap(Map<String, dynamic> json) {
return ColorDetails(json['color_code'], json['color_label']);
}
Map toJson() {
return {
'color_code': code,
'color_label': label,
};
}
}
In case you are interested, you can checkout the article I have written as well at https://blog.litedevs.com/infinite-scroll-list-using-flutter-firebase-firestore
I am using a FutureBuilder to display data from a sqlite database. I can add data to that database using a drawer that is also on that page. When I add new data I want the FutureBuilder to automatically update so the page will display the new data that was just added to the sqlite database. How could I properly go about doing this? Thanks!
The page where the data is displayed using the FutureBuilder
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[200],
appBar: PreferredSize(
preferredSize: Size.fromHeight(95.0),
child: AppBar(
automaticallyImplyLeading: false, // hides leading widget
flexibleSpace: DataAppBar(),
),
),
body: FutureBuilder<dynamic>(
future: DataDBProvider.dataDB.getData(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
print(
'${snapshot.error}',
);
}
}
List data = snapshot.data;
return ListView.builder(
itemCount: data.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 1.0, horizontal: 4.0),
child: Card(
color: (index % 2 == 0) ? greycolor : Colors.white,
child: Container(
height: 60,
padding: EdgeInsets.fromLTRB(0, 20, 0, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Flexible(
flex: 3,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(left: 4),
child: Text(data[index].date,
style: TextStyle(fontSize: 14),
textAlign: TextAlign.left),
),
],
),
),
Expanded(
flex: 5,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(data[index].title,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: Colors.black,
fontFamily: 'Montserrat'),
textAlign: TextAlign.center)
],
)),
],
),),
Expanded(
flex: 3,
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('\$${data[index].amount}',
style: TextStyle(fontSize: 17,
color: Colors.black),
textAlign: TextAlign.right),
],
),
),
],
)),
),
);
},
);
}
));
}
You can copy paste run full code below
To achieve automatically update so the page will display the new data
In this case, you can use package https://pub.dev/packages/sqlbrite and StreamBuilder
sqlbrite is Streaming sqflite, The BriteDatabase.createQuery method is similar to Database.query. Listen to the returned Stream<Query> which will immediately notify with a Query to run.
And the page will automatically display the new data
code snippet
class AppDb {
...
final _dbFuture = _open().then((db) => BriteDatabase(db));
Stream<List<Item>> getAllItems() async* {
final db = await _dbFuture;
yield* db
.createQuery(_tableItems, orderBy: 'createdAt DESC')
.mapToList((json) => Item.fromJson(json));
}
Future<bool> insert(Item item) async {
final db = await _dbFuture;
final id = await db.insert(
_tableItems,
item.toJson(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
return id != -1;
}
Future<bool> remove(Item item) async {
...
}
Future<bool> update(Item item) async {
...
}
}
...
StreamBuilder<List<Item>>(
stream: AppDb.getInstance().getAllItems(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
final items = snapshot.data;
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
working demo
full code
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:sqlbrite/sqlbrite.dart';
import 'dart:math';
const _tableItems = 'items';
Future<Database> _open() async {
final directory = await getApplicationDocumentsDirectory();
final path = join(directory.path, 'example.db');
return await openDatabase(
path,
version: 1,
onCreate: (Database db, int version) async {
await db.execute(
'''
CREATE TABLE $_tableItems(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
content TEXT NOT NULL,
createdAt TEXT NOT NULL
)
''',
);
final batch = db.batch();
for (int i = 0; i < 10; i++) {
batch.insert(
_tableItems,
Item(
null,
contents.random(),
DateTime.now(),
).toJson(),
);
}
final list = await batch.commit(
continueOnError: true,
noResult: false,
);
print('Batch result: $list');
},
);
}
class AppDb {
static AppDb _singleton;
AppDb._();
factory AppDb.getInstance() => _singleton ??= AppDb._();
final _dbFuture = _open().then((db) => BriteDatabase(db));
Stream<List<Item>> getAllItems() async* {
final db = await _dbFuture;
yield* db
.createQuery(_tableItems, orderBy: 'createdAt DESC')
.mapToList((json) => Item.fromJson(json));
}
Future<bool> insert(Item item) async {
final db = await _dbFuture;
final id = await db.insert(
_tableItems,
item.toJson(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
return id != -1;
}
Future<bool> remove(Item item) async {
final db = await _dbFuture;
final rows = await db.delete(
_tableItems,
where: 'id = ?',
whereArgs: [item.id],
);
return rows > 0;
}
Future<bool> update(Item item) async {
final db = await _dbFuture;
final rows = await db.update(
_tableItems,
item.toJson(),
where: 'id = ?',
whereArgs: [item.id],
conflictAlgorithm: ConflictAlgorithm.replace,
);
return rows > 0;
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData.dark(),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
final _dateFormatter = DateFormat.Hms().add_yMMMd();
MyHomePage({Key key}) : super(key: key);
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('sqlbrite example'),
),
body: Container(
constraints: BoxConstraints.expand(),
child: StreamBuilder<List<Item>>(
stream: AppDb.getInstance().getAllItems(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
final items = snapshot.data;
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return ListTile(
title: Text(item.content),
subtitle:
Text('Created: ${_dateFormatter.format(item.createdAt)}'),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.remove_circle),
onPressed: () => _remove(item),
),
IconButton(
icon: Icon(Icons.edit),
onPressed: () => _update(item),
),
],
),
);
},
);
},
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: _add,
),
);
}
void _add() async {
final item = Item(
null,
contents.random(),
DateTime.now(),
);
final success = await AppDb.getInstance().insert(item);
print('Add: $success');
}
void _remove(Item item) async {
final success = await AppDb.getInstance().remove(item);
print('Remove: $success');
}
void _update(Item item) async {
final success = await AppDb.getInstance().update(
item.copyWith(
contents.random(),
),
);
print('Update: $success');
}
}
const contents = [
'Aaren',
'Aarika',
'Abagael',
'Abagail',
'Abbe',
'Abbey',
'Abbi',
'Abbie',
'Abby',
'Abbye',
'Abigael',
'Abigail',
'Abigale',
'Abra',
'Ada',
'Adah',
'Adaline',
'Adan',
'Adara',
'Adda',
'Addi',
'Addia',
'Addie',
'Addy',
'Adel',
'Adela',
'Adelaida',
'Adelaide',
'Adele',
'Adelheid',
'Adelice',
'Adelina',
'Adelind',
'Adeline',
'Adella',
'Adelle',
'Adena',
'Adey',
'Adi',
'Adiana',
'Adina',
'Adora',
'Adore',
'Adoree',
'Adorne',
'Adrea',
'Adria',
'Adriaens',
'Adrian',
'Adriana',
'Adriane',
'Adrianna',
'Adrianne',
'Adriena',
'Adrienne',
'Aeriel',
'Aeriela',
'Aeriell',
'Afton',
'Ag',
'Agace',
'Agata',
'Agatha',
'Agathe',
'Aggi',
'Aggie',
'Aggy',
'Agna',
'Agnella',
'Agnes',
'Agnes',
];
extension RandomElementExtension<T> on List<T> {
T random() {
final index = Random().nextInt(length);
return this[index];
}
}
class Item {
final int id;
final String content;
final DateTime createdAt;
const Item(
this.id,
this.content,
this.createdAt,
);
factory Item.fromJson(Map<String, dynamic> json) {
return Item(
json['id'],
json['content'],
DateTime.parse(json['createdAt']),
);
}
Map<String, dynamic> toJson() {
return {
if (id != null) 'id': id,
'content': content,
'createdAt': createdAt.toIso8601String(),
};
}
Item copyWith(String content) => Item(id, content, createdAt);
#override
bool operator ==(Object other) =>
identical(this, other) ||
other is Item &&
runtimeType == other.runtimeType &&
id == other.id &&
content == other.content &&
createdAt == other.createdAt;
#override
int get hashCode => id.hashCode ^ content.hashCode ^ createdAt.hashCode;
#override
String toString() =>
'Item{id: $id, content: $content, createdAt: $createdAt}';
}
I'm using a method of searching for an api but I'm not managing to pass the information I get to the Widget that renders it and get the data I all well
Header search Widget:
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:movies/Movies/bloc/blocmovies.dart';
import 'package:movies/Movies/model/findmoviemodel.dart';
import 'package:movies/Movies/ui/widgets/gridview_search_screen.dart';
class HeaderSearchScreen extends StatefulWidget with PreferredSizeWidget {
HeaderSearchScreen({Key key}) : super(key: key);
#override
_HeaderSearchScreenState createState() => _HeaderSearchScreenState();
#override
Size get preferredSize => Size.fromHeight(kToolbarHeight);
}
class _HeaderSearchScreenState extends State<HeaderSearchScreen> {
BlocMovies blocMovies;
final TextEditingController _controller = TextEditingController();
Widget appBarTitle() {
return TextField(
onSubmitted: searchOperation,
autofocus: true,
controller: _controller,
style: TextStyle(
color: Colors.black,
),
decoration: InputDecoration(
suffix: IconButton(
icon: Icon(Icons.cancel),
onPressed: () {
Future.delayed(Duration(milliseconds: 50)).then((_) {
_controller.clear();
FocusScope.of(context).unfocus();
});
},
),
hintText: "Buscar",
hintStyle: TextStyle(color: Colors.grey.withOpacity(0.5))),
);
}
#override
Widget build(BuildContext context) {
blocMovies = BlocProvider.of(context);
return buildAppBar(context);
}
Widget buildAppBar(BuildContext context) {
return AppBar(
iconTheme: IconThemeData(color: Colors.black),
backgroundColor: Colors.white,
centerTitle: true,
title: this.appBarTitle());
}
searchOperation(String searchText) {
blocMovies.findMovies(searchText)
.then((data){
if(data.results.length == 0){
print("no se encontro");
showDialog(
context: context,
child: AlertDialog(
title: const Text("No se encontro la pelicula"),
actions: [
FlatButton(
child: const Text("Ok"),
onPressed: () => Navigator.pop(context),
),
],
),
);
}else{
setState(() {
print(data.results);
GridViewSearchScreen(listsearchmovieOne: data.results);
});
}
})
.catchError((){
print("Hubo un error");
});
}
}
/*
blocMovies.findMovies(searchText)
.then((data){
if(data.results.length == 0){
print("no se encontro");
showDialog(
context: context,
child: AlertDialog(
title: const Text("No se encontro la pelicula"),
actions: [
FlatButton(
child: const Text("Ok"),
onPressed: () => Navigator.pop(context),
),
],
),
);
}else{
//Here trying pass datan and debug console a get snapshot
GridViewSearchScreen(listsearchmovieOne: data.results);
}
})
.catchError((){
print("Hubo un error");
});
GridViewSearch:
import 'package:animate_do/animate_do.dart';
import 'package:flutter/material.dart';
import 'package:movies/Movies/model/findmoviemodel.dart';
import 'package:movies/Movies/ui/widgets/cadsearchmovies.dart';
import 'package:movies/Widgets/Screen_Sizes/responsive_screens.dart';
class GridViewSearchScreen extends StatefulWidget {
List<Result> listsearchmovieOne;
double _crossAxisSpacing = 15, _mainAxisSpacing = 12, _aspectRatio = 1;
GridViewSearchScreen({Key key, this.listsearchmovieOne}) : super(key: key);
#override
_GridViewSearchScreenState createState() => _GridViewSearchScreenState(listsearchmovie: this.listsearchmovieOne );
}
class _GridViewSearchScreenState extends State<GridViewSearchScreen> {
List<Result> listsearchmovie;
_GridViewSearchScreenState({this.listsearchmovie});
#override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
double screenHeight = MediaQuery.of(context).size.height;
double _pixeRatio = MediaQuery.of(context).devicePixelRatio;
bool small = ResponsiveWidget.isScreenSmall(screenWidth, _pixeRatio);
bool medium = ResponsiveWidget.isScreenMedium(screenWidth, _pixeRatio);
bool large = ResponsiveWidget.isScreenLarge(screenWidth, _pixeRatio);
return (listsearchmovie == null)
? Center(
child: Container(
child: Text("No hay peliculas que mostrar"),
))
: Container(
margin: EdgeInsets.only(top: screenHeight * 0.2),
child: GridView.builder(
itemCount: listsearchmovie.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: (large) ? 4 : (medium) ? 2 : (small) ? 2 : 2,
crossAxisSpacing: widget._crossAxisSpacing,
mainAxisSpacing: widget._mainAxisSpacing,
childAspectRatio: (large)
? screenWidth / (screenHeight / 0.62)
: (medium)
? screenWidth / (screenHeight / 1.03)
: (small)
? screenWidth / (screenHeight / 1.03)
: screenWidth / (screenHeight / 1.03),
),
itemBuilder: (BuildContext context, int i) {
final movie = listsearchmovie[i];
print(movie.posterPath);
return FadeInLeft(
duration: Duration(milliseconds: 10 * i),
child: CardSearchinfoMovies(
movie: Result(
backdropPath: movie.backdropPath,
overview: movie.overview,
posterPath: movie.posterPath,
voteAverage: movie.voteAverage,
title: movie.title)),
);
}));
}
}
and on this screen is where together these two widgets
import 'package:flutter/material.dart';
import 'package:movies/Movies/ui/widgets/gridview_search_screen.dart';
import 'package:movies/Movies/ui/widgets/header_search_screen.dart';
class ScreenSearchMovies extends StatefulWidget {
#override
_ScreenSearchMoviesState createState() => _ScreenSearchMoviesState();
}
class _ScreenSearchMoviesState extends State<ScreenSearchMovies> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: HeaderSearchScreen(),
body: Stack(
children:<Widget>[
GridViewSearchScreen()
]
)
);
}
}
Any idea what might be going on already trying everything and I can't get the information displayed?
Try this way:
`....
....
class _GridViewSearchScreenState extends State<GridViewSearchScreen> {
List<Result> listsearchmovie = List<Result>();
_GridViewSearchScreenState({this.listsearchmovie});
.....
.....
`
I using firebase to save my data and use flutter (dart) to access this data
Now. I have collection have the name (Customer_payment_details) I put random unique ID like this (tZ9d9cYeUvXKm1easHtr) and in this ID I have Sub Collection (Payment_info). in this subcollection have a unique ID. And in this id have many values.
I want to read this data as List
I write this code to access this data
This Code Belo Is My model name (Customer_payment_details)
class Customer_payment_details {
String customer_id;
String payment_date;
String stay_balance;
String amount_balance;
String customer_note;
String dept_now;
String id;
Customer_payment_details({ this.id ,this.customer_id, this.payment_date, this.stay_balance ,
this.customer_note , this.amount_balance , this.dept_now });
Customer_payment_details.fromMap_details(Map snapshot,String id) :
id = id ?? '',
customer_id = snapshot['customer_id'] ?? '',
payment_date = snapshot['payment_date'] ?? '',
stay_balance = snapshot['stay_balance'] ?? '',
amount_balance =snapshot["amount_balance"] ?? '',
customer_note = snapshot['customer_note'],
dept_now = snapshot['dept_now'];
toJson() {
return {
"customer_id" : customer_id,
"payment_date": payment_date,
"stay_balance" : stay_balance,
"amount_balance" : amount_balance,
"customer_note" : customer_note,
"dept_now" : dept_now,
};
}
}
This is my API:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:async';
class payment_api {
final Firestore _db = Firestore.instance;
final String path;
CollectionReference ref;
payment_api(this.path) {
ref = _db.collection(path);
}
Future<QuerySnapshot> getDataCollection() {
return ref.getDocuments();
}
Stream<QuerySnapshot> streamDataCollection() {
return ref.snapshots();
}
Future<DocumentSnapshot> getDocumentById(String id) {
return ref.document(id).get();
}
Future<void> removeDocument(String id) {
return ref.document(id).delete();
}
Future<DocumentReference> addDocument(Map data, String customer_id) {
return ref.document(customer_id).collection("Payment_info").add(data);
}
Future<void> updateDocument(Map data, String id) {
return ref.document(id).updateData(data);
}
}
This is my screen:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:login_example/CustomerCore/models/cstomer_payment_detailsModel.dart';
import 'package:login_example/CustomerCore/models/productModel.dart';
import 'package:login_example/CustomerCore/viewmodels/CRUDModel.dart';
import 'package:login_example/CustomerCore/viewmodels/customer_paymentCRUD.dart';
import 'package:login_example/uiCustomer/widgets/payment_details_card.dart';
import 'package:login_example/uiCustomer/widgets/productCard.dart';
import 'package:provider/provider.dart';
class payment_details_view extends StatefulWidget {
#override
_payment_details_viewState createState() => _payment_details_viewState();
}
class _payment_details_viewState extends State<payment_details_view> {
List<Customer_payment_details> customers_information;
#override
Widget build(BuildContext context) {
final productProvider = Provider.of<customer_paymentCRUD>(context);
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepPurple,
title: Center(child: Text('ايوب محمد ابراهيم')),
),
body: Container(
child: StreamBuilder(
stream: productProvider.fetchcustomer_paymentsAsStream(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
customers_information = snapshot.data.documents
.map((doc) => Customer_payment_details.fromMap_details(doc.data,
doc.documentID)).toList();
return ListView.builder(
itemCount: customers_information.length,
itemBuilder: (buildContext, index) => payment_details_card(
customer_details: customers_information[index]),
);
This is my card:
class payment_details_card extends StatelessWidget {
final Customer_payment_details customer_details;
MoneyFormatterOutput fmf;
String convert_value (String value){
fmf = FlutterMoneyFormatter(
amount: double.parse(value),
settings: MoneyFormatterSettings(
symbol: 'د.ع',
thousandSeparator: ',',
decimalSeparator: '.',
symbolAndNumberSeparator: ' ',
fractionDigits: 0,
compactFormatType: CompactFormatType.short
)
).output;
return fmf.symbolOnLeft;
}
payment_details_card({#required this.customer_details});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: (){
// Navigator.push(context, MaterialPageRoute(builder: (_) => CustomerProfilePage(customer_info:
customer_details)));
print(customer_details.id);
},
child: Padding(
padding: EdgeInsets.all(8),
child: Card(
elevation: 10,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15.0),
),
child: Container(
height: MediaQuery
.of(context)
.size
.height * 0.30,
width: MediaQuery
.of(context)
.size
.width * 0.9,
child: Column(
children: <Widget>[
Hero(
tag: customer_details.customer_id,
child : Material(
color: Colors.white70,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(right: 10.0 , top: 15.0),
child: Text(
//اسم الزبون
customer_details.customer_id,
textDirection: TextDirection.rtl,
style: TextStyle(
fontFamily: 'JannaLT-Regular',
fontWeight: FontWeight.w900,
fontSize: 22,
color: Colors.black,
),
),
),
etc...
The image below shows the database:
and this image for subcollection
I want to load this data as listview in card. Can anyone help for this?
Check this code.
stream: productProvider.fetchcustomer_paymentsAsStream(),
builder: (context,snapshot) {
if (snapshot.hasData) {
List<Customer_payment_details> customers_information= snapshot.data;
return ListView.builder(
itemCount: customers_information.length,
itemBuilder: (context, index){
return YourCardClass(
customers_information: customers_information[index]);
}