flutter, ensure the elavated button stay at the bottom of the screen despite streambuilder built a lot of data - flutter

What im trying to do is showing a list of assignments and have buttons at the bottom to let the user to select which function they want but when the data becomes a lot and my buttons got push below the screen, i was hoping for the buttons to stay on the screen no matter how many data is generated, buttons will not get push down and the button does not block the visuals of the data, i got an image below to show that
example at here
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:fyp/assignment/ViewComplete.dart';
import 'ReassignEmployee.dart';
import 'ViewAssigned.dart';
import 'ViewUnassigned.dart';
class assignmentPage extends StatefulWidget {
const assignmentPage({Key? key}) : super(key: key);
#override
State<assignmentPage> createState() => _assignmentPageState();
}
class _assignmentPageState extends State<assignmentPage> {
TextEditingController searchController = TextEditingController();
String searchText = '';
String id = '';
CollectionReference allNoteCollection =
FirebaseFirestore.instance.collection('Assignment');
List<DocumentSnapshot> documents = [];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('View Assignment'),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
TextField(
controller: searchController,
onChanged: (value) {
setState(() {
searchText = value;
});
},
decoration: InputDecoration(
hintText: 'Search...',
prefixIcon: Icon(Icons.search),
),
),
StreamBuilder(
stream: allNoteCollection.snapshots(),
builder: (ctx, streamSnapshot) {
if (streamSnapshot.connectionState ==
ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator());
}
documents = streamSnapshot.data!.docs;
if (searchText.length > 0) {
documents = documents.where((element)
{
return
( element.get('carPlate').toString().
toLowerCase().contains(searchText.toLowerCase()) ||
element.get('custName').toString().
toLowerCase().contains(searchText.toLowerCase())||
element.get('date').toString().
toLowerCase().contains(searchText.toLowerCase())||
element.get('employee').toString().
toLowerCase().contains(searchText.toLowerCase())||
element.get('id').toString().
toLowerCase().contains(searchText.toLowerCase())||
element.get('payment').toString().
toLowerCase().contains(searchText.toLowerCase())||
element.get('serviceName').toString().
toLowerCase().contains(searchText.toLowerCase())||
element.get('status').toString().
toLowerCase().contains(searchText.toLowerCase())||
element.get('timeEnd').toString().
toLowerCase().contains(searchText.toLowerCase())||
element.get('timeStart').toString().
toLowerCase().contains(searchText.toLowerCase())
);
}).toList();
}
return ListView.separated(
reverse: true,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: documents.length,
separatorBuilder: (BuildContext context, int index) {
return Divider();
},
itemBuilder: (BuildContext context, int index) {
id = documents[index]['id'];
return ListTile(
contentPadding:
EdgeInsets.symmetric(horizontal: 0.0),
onTap: () {
},
title: Column(
children: <Widget>[
Text(documents[index]['carPlate']),
Text(documents[index]['custName']),
Text(documents[index]['date']),
Text(documents[index]['employee']),
Text(documents[index]['payment']),
Text(documents[index]['serviceName']),
Text(documents[index]['status']),
Text(documents[index]['timeStart']),
Text(documents[index]['timeEnd']),
],
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
InkWell(
onTap: (){
Navigator.push(context,
MaterialPageRoute(builder: (context) => ReassignEmployee(
serviceName: documents[index]['serviceName'],
carPlate: documents[index]['carPlate'],
custName: documents[index]['custName'],
date: documents[index]['date'],
timeStart: documents[index]['timeStart'],
timeEnd: documents[index]['timeEnd'],
payment: documents[index]['payment'],
status: documents[index]['status'],
employee: documents[index]['employee'],
id: documents[index]['id'],
)));
},child: Icon(Icons.edit),
),
],
)
);
},
);
},
),
ElevatedButton(
onPressed: () async {
Navigator.push(context,
MaterialPageRoute(builder: (context) => ViewUnassigned(id: id.toString(),)));
},
child: Text('Show unassigned'),
),
ElevatedButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => ViewAssigned()));
},
child: Text('Show assigned'),
),
],
),
),
);
}
}
I have no idea how to do it, hope someone can guide me, really thanks a lot

You can use Stack and Positioned to pin buttons to the bottom of screen, like this:
class assignmentPage extends StatefulWidget {
const assignmentPage({Key? key}) : super(key: key);
#override
State<assignmentPage> createState() => _assignmentPageState();
}
class _assignmentPageState extends State<assignmentPage> {
TextEditingController searchController = TextEditingController();
String searchText = '';
String id = '';
CollectionReference allNoteCollection =
FirebaseFirestore.instance.collection('Assignment');
List<DocumentSnapshot> documents = [];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('View Assignment'),
),
body: Stack(
children: [
SingleChildScrollView(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
TextField(
controller: searchController,
onChanged: (value) {
setState(() {
searchText = value;
});
},
decoration: InputDecoration(
hintText: 'Search...',
prefixIcon: Icon(Icons.search),
),
),
StreamBuilder(
stream: allNoteCollection.snapshots(),
builder: (ctx, streamSnapshot) {
if (streamSnapshot.connectionState ==
ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
documents = streamSnapshot.data!.docs;
if (searchText.length > 0) {
documents = documents.where((element) {
return (element
.get('carPlate')
.toString()
.toLowerCase()
.contains(searchText.toLowerCase()) ||
element
.get('custName')
.toString()
.toLowerCase()
.contains(searchText.toLowerCase()) ||
element
.get('date')
.toString()
.toLowerCase()
.contains(searchText.toLowerCase()) ||
element
.get('employee')
.toString()
.toLowerCase()
.contains(searchText.toLowerCase()) ||
element
.get('id')
.toString()
.toLowerCase()
.contains(searchText.toLowerCase()) ||
element
.get('payment')
.toString()
.toLowerCase()
.contains(searchText.toLowerCase()) ||
element
.get('serviceName')
.toString()
.toLowerCase()
.contains(searchText.toLowerCase()) ||
element
.get('status')
.toString()
.toLowerCase()
.contains(searchText.toLowerCase()) ||
element
.get('timeEnd')
.toString()
.toLowerCase()
.contains(searchText.toLowerCase()) ||
element
.get('timeStart')
.toString()
.toLowerCase()
.contains(searchText.toLowerCase()));
}).toList();
}
return ListView.separated(
reverse: true,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: documents.length,
separatorBuilder: (BuildContext context, int index) {
return Divider();
},
itemBuilder: (BuildContext context, int index) {
id = documents[index]['id'];
return ListTile(
contentPadding:
EdgeInsets.symmetric(horizontal: 0.0),
onTap: () {},
title: Column(
children: <Widget>[
Text(documents[index]['carPlate']),
Text(documents[index]['custName']),
Text(documents[index]['date']),
Text(documents[index]['employee']),
Text(documents[index]['payment']),
Text(documents[index]['serviceName']),
Text(documents[index]['status']),
Text(documents[index]['timeStart']),
Text(documents[index]['timeEnd']),
],
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ReassignEmployee(
serviceName: documents[index]
['serviceName'],
carPlate: documents[index]
['carPlate'],
custName: documents[index]
['custName'],
date: documents[index]
['date'],
timeStart: documents[index]
['timeStart'],
timeEnd: documents[index]
['timeEnd'],
payment: documents[index]
['payment'],
status: documents[index]
['status'],
employee: documents[index]
['employee'],
id: documents[index]['id'],
)));
},
child: Icon(Icons.edit),
),
],
));
},
);
},
),
],
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Column(
children: [
ElevatedButton(
onPressed: () async {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ViewUnassigned(
id: id.toString(),
)));
},
child: Text('Show unassigned'),
),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ViewAssigned()));
},
child: Text('Show assigned'),
),
],
))
],
),
);
}
}

Related

How can i separate the selected item on a DropDownButton?

Right now I'm trying to make a dialog where I can change the role for the users that has been logged in on my app, for that i have a ListView with Cards that shows up the information of the user and a DropDownButton to select the role that a can assign to that user.
But when i select a role or item it change on all the user's Card.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
final user = FirebaseAuth.instance.currentUser!;
String role = '1';
Stream<QuerySnapshot> readUsers() =>
FirebaseFirestore.instance.collection('users').snapshots();
Future accDialog(BuildContext context) => showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (BuildContext context, setState) => Dialog(
child: Container(
margin: const EdgeInsets.all(24),
child: StreamBuilder(
stream: readUsers(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
roleFunction(String q) {
setState(() {
role = q;
});
}
if (snapshot.hasError) {
return const Text('Algo ha salido mal!');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Cargando");
}
return ListView(
shrinkWrap: true,
children: snapshot.data!.docs
.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return Card(
margin: const EdgeInsets.all(8),
child: Padding(
padding: const EdgeInsets.all(8),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
data['name'],
style:
Theme.of(context).textTheme.titleMedium,
),
Text('Correo: ${data['email']}'),
Text('Rol Actual: ${data['role']}'),
Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: RoleMenu(
roleFunction: roleFunction)),
const Spacer(),
ElevatedButton(
onPressed: () {
final setRole = <String, String>{
"role": role,
};
FirebaseFirestore.instance
.collection('users')
.doc(document.id)
.set(setRole,
SetOptions(merge: true));
},
child: const Text('Guardar'))
],
)
],
),
),
);
})
.toList()
.cast(),
);
},
),
),
),
),
);
class RoleMenu extends StatefulWidget {
final Function roleFunction;
const RoleMenu({super.key, required this.roleFunction});
#override
State<RoleMenu> createState() => _RoleMenuState();
}
class _RoleMenuState extends State<RoleMenu> {
String selected = '';
#override
Widget build(BuildContext context) {
return DropdownButtonHideUnderline(
child: DropdownButton<String>(
enableFeedback: true,
dropdownColor: ElevationOverlay.applySurfaceTint(
Theme.of(context).colorScheme.surface,
Theme.of(context).colorScheme.surfaceTint,
2),
iconEnabledColor: Theme.of(context).colorScheme.onSurfaceVariant,
style: Theme.of(context).textTheme.labelLarge,
items: const [
DropdownMenuItem<String>(
value: 'User',
child: Text('Usuario'),
),
DropdownMenuItem<String>(
value: 'Admin',
child: Text('Admin'),
),
],
value: selected,
onChanged: (value) {
setState(() {
selected = value!;
widget.roleFunction(selected);
});
},
),
);
}
}

Filter StreamBuilder snapshot data - flutter

When the user enters 1st value and 2nd value and clicks the submit button, StreamBuilder should output the data between the values (age filter). How to do this?
Here is picture of display. and my code - below the picture
Full page code:
class TestScreen extends StatefulWidget {
const TestScreen({super.key});
#override
State<TestScreen> createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> {
final _firstTextController = TextEditingController();
final _secondTextController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: SafeArea(
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection("users")
// .orderBy("accountCreated")
.where("activeStatus", isEqualTo: "active")
// .where("uid", isNotEqualTo: FirebaseAuth.instance.currentUser!.uid)
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (streamSnapshot.connectionState == ConnectionState.waiting ||
!streamSnapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length + 1,
itemBuilder: (context, index) {
if (index == 0) {
return Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _firstTextController,
decoration: const InputDecoration(
hintText: "from",
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _secondTextController,
decoration: const InputDecoration(
hintText: "to",
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextButton(
onPressed: () {
print("First value: ${_firstTextController.text}");
print(
"Second value: ${_secondTextController.text}");
},
child: const Text("Submit"),
),
),
],
);
} else {
return Text(
streamSnapshot.data!.docs[index - 1]["displayName"]);
}
},
);
},
),
),
);
}
}
class TestScreen extends StatefulWidget {
const TestScreen({super.key});
#override
State<TestScreen> createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> {
int userLowerValue = 20;
int userUpperValue = 60;
Stream<QuerySnapshot> getDataStream(int lower, int upper) {
return FirebaseFirestore.instance
.collection("users")
.where("age", isGreaterThan: lower)
.where("age", isLessThan: upper)
.snapshots();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
onPressed: () {
int lower = 20;
int upper = 60;
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Enter Filter Values"),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
TextFormField(
keyboardType: TextInputType.number,
decoration:
InputDecoration(labelText: "Lower Value"),
onChanged: (value) {
lower = int.parse(value);
},
),
TextFormField(
keyboardType: TextInputType.number,
decoration:
InputDecoration(labelText: "Upper Value"),
onChanged: (value) {
upper = int.parse(value);
},
),
],
),
),
actions: <Widget>[
TextButton(
child: Text("Apply"),
onPressed: () {
Navigator.of(context).pop();
setState(() {
userLowerValue = lower;
userUpperValue = upper;
});
},
),
],
);
},
);
},
icon: const Icon(Icons.filter))
],
),
body: SafeArea(
child: StreamBuilder(
stream: getDataStream(userLowerValue, userUpperValue),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (streamSnapshot.connectionState == ConnectionState.waiting ||!streamSnapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (streamSnapshot.data!.docs.isEmpty) {
return const Center(
child: CustomText(
text: "No results found",
size: 30,
),
);
}
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length,
itemBuilder: (context, index) {
return Text(streamSnapshot.data!.docs[index]["displayName"]);
},
);
},
),
),
);
}
}

Flutter dropdownbutton changed value is not displaying

I'm new in flutter. I've created a simple project.
It is fetching documents of person collection from cloud firestore.
There is a modal screen to create new person document (it is opening When I touch the + button)
I have a problem In that modalBottomSheet
I can see the new value of department dropDownButton on the log screen but user interface are not changing.
I think it is related to 'context' but I couldn't solve the problem
Here is my code:
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class Person extends StatefulWidget {
const Person({Key? key}) : super(key: key);
#override
_PersonState createState() => _PersonState();
}
class _PersonState extends State<Person> {
final TextEditingController _nameController = TextEditingController();
final CollectionReference _person = FirebaseFirestore.instance.collection('person');
final CollectionReference _department = FirebaseFirestore.instance.collection('department');
String? _usersDeptName;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Expanded(
child: StreamBuilder(
stream: _person.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (streamSnapshot.hasData) {
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length,
itemBuilder: (context, index) {
final DocumentSnapshot documentSnapshot = streamSnapshot.data!.docs[index];
return Card(
margin: const EdgeInsets.all(10),
child: ListTile(
title: Text(documentSnapshot['personName']),
subtitle: Text(documentSnapshot['departmentName'] ?? '?'),
),
);
},
);
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => {_create()},
child: const Icon(Icons.add),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat);
}
Future<void> _create() async {
_usersDeptName = null;
await showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (BuildContext ctx) {
return Padding(
padding: EdgeInsets.only(top: 20, left: 20, right: 20, bottom: MediaQuery.of(context).viewInsets.bottom + 20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(controller: _nameController, decoration: InputDecoration(labelText: 'person_name'.tr())),
const SizedBox(height: 10),
StreamBuilder<QuerySnapshot>(
stream: _department.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text("loading").tr();
} else {
List<DropdownMenuItem> departments = [];
int? howManyRecords = snapshot.data?.size;
for (int i = 0; i < howManyRecords!; i++) {
DocumentSnapshot snap = snapshot.data?.docs[i] as DocumentSnapshot<Object?>;
departments.add(DropdownMenuItem(child: Text(snap.get('departmentName')), value: snap.get('departmentName')));
}
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: DropdownButton(
value: _usersDeptName,
items: departments,
onChanged: (newValue) {
setState(() {
_usersDeptName = newValue.toString();
print('$_usersDeptName is selected');
});
},
isExpanded: true,
),
),
],
);
}
},
),
ElevatedButton(
child: const Text('save').tr(),
onPressed: () async {
final String name = _nameController.text;
if (name != null) {
await _person.add({"personName": name, 'departmentName': _usersDeptName});
_nameController.text = '';
Navigator.of(context).pop();
}
},
)
],
),
);
},
);
}
}
Try using StatefulBuilder to update the bottomSheet state.
Future<void> _create() async {
_usersDeptName = null;
await showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (BuildContext ctx) {
return StatefulBuilder(
builder: (context, setState) => Padding(
padding: EdgeInsets.only(
If you like to change the widget-state(main UI) at the same time, you can rename the StatefulBuilder's setState and call both on onChanged.
when setting up the value for the _usersDeptName, you are converting it to string, this is not right because now the items and the selected items is 2 different thing,
so if you want it to be the department name, then when setting up the value for _usersDeptName, make it bind to the department name:
example
setState(() {
_usersDeptName = newValue.departmentName;
print('$_usersDeptName is selected');
});

RangeError (index) Flutter

I am getting an error and red screen while trying to click a button. RangeError (index): Invalid value: Valid value range is empty: 0. I do not know how to fix this error because nothing is flagged until I run my emulator. I have attached my
If I need to add some code please let me know I am more than willing to. Thank you!
home_page.dart
import 'package:flutter/material.dart';
import 'package:timestudy_test/pages/study_page.dart';
import 'package:timestudy_test/pages/timer_page.dart';
import 'package:timestudy_test/viewmodels/study_viewmodel.dart';
class HomePage extends StatefulWidget {
#override
State createState() => HomePageState();
}
class HomePageState extends State<HomePage> {
TextEditingController textController = new TextEditingController();
late String filter;
#override
void initState() {
textController.addListener(() {
setState(() {
filter = textController.text;
});
});
super.initState();
}
#override
void dispose() {
textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text('TimeStudyApp'),
),
body: Material(
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 8.0, left: 16.0, right: 16.0),
child: TextField(
style: TextStyle(fontSize: 18.0),
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
suffixIcon: IconButton(
icon: Icon(Icons.close),
onPressed: () {
textController.clear();
FocusScope.of(context).requestFocus(FocusNode());
},
),
hintText: "Search...",
),
controller: textController,
)),
Expanded(
child: StudyViewModel.studies.length > 0
? ListView.builder(
itemCount: StudyViewModel.studies.length,
itemBuilder: (BuildContext context, int index) {
if (filter == null || filter == "") {
return buildRow(context, index);
} else {
if (StudyViewModel.studies[index].name
.toLowerCase()
.contains(filter.toLowerCase())) {
return buildRow(context, index);
} else {
return Container();
}
}
},
)
: Center(
child: Text('No studies found!'),
),
)
],
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () async {
int? nullableInterger;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => StudyPage(
title: 'Add a study',
selected: nullableInterger ?? 0,
)));
},
),
);
}
Widget buildRow(BuildContext context, int index) {
return ExpansionTile(
title: Text(StudyViewModel.studies[index].name, style: TextStyle(fontSize: 18.0)),
children: <Widget>[
ListView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemCount: StudyViewModel.studies[index].tasks.length,
itemBuilder: (context, int taskIndex) {
return ListTile(
title: Text(StudyViewModel.studies[index].tasks[taskIndex].name),
contentPadding: EdgeInsets.symmetric(horizontal: 32.0),
subtitle: Text(
StudyViewModel.studies[index].tasks[taskIndex].elapsedTime),
trailing: IconButton(
icon: Icon(
Icons.timer
),
onPressed: () async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TimerPage(
task: StudyViewModel
.studies[index].tasks[taskIndex])));
},
),
);
},
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(
icon: Icon(
Icons.edit
),
onPressed: () async {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => StudyPage(
title: StudyViewModel.studies[index].name,
selected: index)));
},
),
IconButton(
icon: Icon(
Icons.delete
),
onPressed: () async {
await showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text('Do you wish to delete this study?'),
actions: <Widget>[
FlatButton(
child: Text('Accept'),
onPressed: () async {
StudyViewModel.studies.removeAt(index);
await StudyViewModel.saveFile();
Navigator.of(context).pop();
},
),
FlatButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
});
},
),
],
),
],
);
}
}
study_page.dart
import 'package:flutter/material.dart';
import 'package:timestudy_test/models/study.dart';
import 'package:timestudy_test/models/task.dart';
import 'package:timestudy_test/viewmodels/study_viewmodel.dart';
class StudyPage extends StatefulWidget {
final String title;
final int selected;
StudyPage({required this.title, required this.selected});
#override
State createState() => StudyPageState();
}
class StudyPageState extends State<StudyPage> {
late Study study;
late TextField nameField;
TextEditingController nameController = new TextEditingController();
late TextField taskNameField;
TextEditingController taskNameController = new TextEditingController();
#override
void initState() {
nameField = new TextField(
controller: nameController,
decoration: InputDecoration(
labelText: 'Study name'),
);
taskNameField = new TextField(
controller: taskNameController,
decoration:
InputDecoration(labelText: 'Task name'),
);
if(widget.selected != null) {
study = StudyViewModel.studies[widget.selected];
nameController.text = study.name;
} else {
study = new Study(
name: "",
tasks: <Task>[]
);
}
super.initState();
}
#override
void dispose() {
nameController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text(widget.title),
),
body: Material(
child: Padding(padding: EdgeInsets.all(16.0), child: Column(
children: <Widget>[
Padding(padding: EdgeInsets.only(bottom: 8.0), child: nameField),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Tasks:', style: TextStyle(fontSize: 18.0),),
IconButton(
icon: Icon(Icons.add),
onPressed: () async {
await showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Add a task'),
content: taskNameField,
actions: <Widget>[
FlatButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: Text('Accept'),
onPressed: () {
if(taskNameController.text == ""){
errorDialog(context, 'Please enter a task name!');
} else {
setState(() {
study.tasks.add(new Task(
name: taskNameController.text,
elapsedTime:
StudyViewModel.milliToElapsedString(
0)));
taskNameController.clear();
});
Navigator.of(context).pop();
}
},
),
],
);
});
},
)
],
),
Expanded(
child: ListView.builder(
itemCount: study.tasks.length,
itemBuilder: (context, int index) {
return ListTile(
title: Text(study.tasks[index].name),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
setState(() {
study.tasks.removeAt(index);
});
},
),
);
},
),
), Spacer(),
Center(
child: RaisedButton(
color: Theme.of(context).accentColor,
child: Text('Save'),
onPressed: () async {
if (nameController.text == "") {
errorDialog(context, 'Please enter a study name!');
} else {
if (study.tasks.length < 1) {
errorDialog(context, 'Please add at least one task!');
} else {
study.name = nameController.text;
if (widget.selected != null) {
StudyViewModel.studies[widget.selected] = study;
await StudyViewModel.saveFile();
Navigator.of(context).pop();
} else {
if (StudyViewModel.checkName(nameController.text)) {
errorDialog(context, 'Study name already taken!');
} else {
StudyViewModel.studies.add(study);
await StudyViewModel.saveFile();
Navigator.of(context).pop();
}
}
}
}
},
))
],
),
)));
}
void errorDialog(BuildContext context, String message) async {
await showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text(message),
actions: <Widget>[
FlatButton(
child: Text('Close'),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
}
);
}
}
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () async {
int? nullableInterger;
// the issue is here you need to assign value
// the nullableInterger is use here as nothing.. declare it on state level and
// assign it when your listview builder done so the value of nullable integer is
// changed and passing value as argument will not be 0 and this error will not appear again
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => StudyPage(
title: 'Add a study',
selected: nullableInterger ?? 0,
)));
},
),

Clear list when page is loaded

I got a page(SearchComparePage) that has a few listTiles with checkboxes. When the FloatingActionButton is pressed, it sends the "mockIdList" which is a list that gets things added to it when the checkboxes are checked. So my problem is that when I press the button and send the list to the other page and I move back to my SearchComparePage, then the mockIdList is still filled with the data from the checked boxes from earlier. Is it possible to somehow clear the mockIdList everytime the page is loaded?
Im sorry if my question is weird. I'm not good at asking questions.
class SearchComparePage extends StatefulWidget{
SearchComparePage({Key key, this.title}) : super(key: key);
final String title;
_SearchState createState()=> _SearchState();
}
class _SearchState extends State<SearchComparePage> {
List<String> mockIdList = [];
String searchString;
String name = "";
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
//backgroundColor: Color(0xFF8BC34A),
appBar: AppBar(
leading: IconButton(
onPressed: () {
Navigator.of(context).pop();
},
icon: Icon(Icons.arrow_back_ios),
color: Colors.white,
),
backgroundColor: Colors.green,
//elevation: 0,
title: Text("Enklere valg"),
),
body: Column(
children: <Widget> [
Expanded(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
onChanged: (value) {
setState(() {
searchString = value.toLowerCase();
});
},
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
contentPadding: EdgeInsets.only(left: 25.0),
hintText: 'Søk etter produkt',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(4.0)
)
),
),
),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
),
child: StreamBuilder<QuerySnapshot>(
stream:
(searchString == null || searchString.trim() == "")
? Firestore.instance
.collection('products')
.snapshots()
: Firestore.instance
.collection("products")
.where("searchIndex",
arrayContains: searchString)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
return new ListView(
children: snapshot.data.documents
.map((DocumentSnapshot document) {
return buildResultCard(context, document);
}).toList(),
);
}
}
)
)
)
],
),
)
],
),
// Button that sends the information in mockIdList to the other page.
floatingActionButton: FloatingActionButton(child: Icon(Icons.add),onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => CompareResultPage(idList: mockIdList,)),
);
},
),
),
);
}
// Widget that builds the listtiles
Widget buildResultCard(context, data) {
bool _isChecked = false;
navigateToDetail(context, data) {
Navigator.push(context, MaterialPageRoute(builder: (context) => ProductPage(product_id: data['product_id'],)));
}
return StatefulBuilder(
builder: (context, StateSetter setState) {
return Center(
child: ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(data['product_image'] ?? ''),
),
title: Text(data['product_maker'] ?? ''),
subtitle: Text(data['product_name'] ?? ''),
trailing: Checkbox(
value: _isChecked,
onChanged: (bool value) {
if (value == true) {
setState(() {
mockIdList.add(data['product_id']);
_isChecked = value;
});
}
if (value == false) {
setState(() {
mockIdList.remove(data['product_id']);
_isChecked = value;
});
}
},
),
onTap: () => navigateToDetail(context, data),
),
);
}
);
}
}
When you return from your other view you'll want to reset your mockIdList
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductPage(
product_id: data['product_id'],
),
),
).then((_) => setState(() => mockIdList.clear())); // <----Add this line of code