How to persist and save value from shared prefernces in Flutter? - flutter

I want to persist value after user leaves page, also I would like to persist selected values, so I found out shared prefernces and I save it locally, but when I left page and return it remains unselected.
So I decided to convert my multipleSelected list to String, because sharedprefernces can't save list of ints and sfter that save selected values in lists. So how can i solve that problem when user leaves page and selected items become unselected.
class DataBaseUser extends StatefulWidget {
const DataBaseUser({Key? key}) : super(key: key);
#override
State<DataBaseUser> createState() => _DataBaseUserState();
}
class _DataBaseUserState extends State<DataBaseUser> {
int index = 1;
/// add selected items from list
List multipleSelected = [];
/// another list to form the new list above previous one
List chosenListsAbove = [];
List basesNames = [];
SharedPreferences? sharedPreferences;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Typographys.primaryColor,
appBar: PreferredSize(
preferredSize: const Size(125, 125),
child: AppBarService(),
),
body: Column(
children: [
// chosenOne(),
Card(
color: Typographys.gradientCard2,
child: ExpansionTile(
iconColor: Colors.white,
maintainState: true,
title: Text(
'Bases',
style: TextStyle(
fontFamily: 'fonts/Montserrat',
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 35),
),
children: [
SizedBox(
height: 10,
),
getDataBaseList(),
SizedBox(
height: 22,
),
getUpdateBaseButtons(),
SizedBox(
height: 10,
),
],
),
),
],
),
);
}
Widget getDataBaseList() {
return FutureBuilder<List>(
future: BasesService().GetBases(),
builder: (context, snapshot) {
List? baseNames = snapshot.data;
print(baseNames);
return ListView.builder(
shrinkWrap: true,
itemCount: baseNames?.length ?? 0,
itemBuilder: (context, i) {
Future<void> _onCategorySelected(bool selected, id) async {
final pref = await SharedPreferences.getInstance();
if (selected == true) {
setState(() {
multipleSelected.add(id);
List<String> stringsList =
multipleSelected.map((i) => i.toString()).toList();
// store your string list in shared prefs
pref.setStringList("stringList", stringsList);
List<String> mList =
(pref.getStringList('stringList') ?? <String>[]);
print('HERE');
print(mList);
print('HERE 2');
});
} else {
setState(
() {
multipleSelected.remove(id);
},
);
}
}
return Column(
children: [
ListTile(
title: Padding(
padding: const EdgeInsets.only(left: 1.0),
child: Text(
baseNames?[i]['name'] ?? 'not loading',
style: TextStyle(
fontFamily: 'fonts/Montserrat',
fontSize: 24,
fontWeight: FontWeight.w900,
color: Colors.white),
),
),
leading: Checkbox(
activeColor: Colors.green,
checkColor: Colors.green,
side: BorderSide(width: 2, color: Colors.white),
value: multipleSelected.contains(
baseNames?[i]['id'],
),
onChanged: (bool? selected) {
_onCategorySelected(selected!, baseNames?[i]['id']);
},
)
//you can use checkboxlistTile too
),
],
);
},
);
},
);
}
Widget getUpdateBaseButtons() {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FutureBuilder<bool>(
future: BasesService().SelectBaseAsync(multipleSelected.cast()),
builder: (context, snapshot) {
return ElevatedButton(
onPressed: () {
if (snapshot.data == true) {
BasesService().SelectBaseAsync(multipleSelected.cast());
print(multipleSelected.cast());
print(multipleSelected);
successSnackBar();
} else {
notSuccessSnackBar();
}
},
child: Text(
'Send bases',
style: TextStyle(
fontFamily: 'fonts/Montserrat',
fontSize: 22,
fontWeight: FontWeight.w900,
color: Colors.white,
letterSpacing: 2),
),
style: ElevatedButton.styleFrom(
minimumSize: Size(200, 40),
primary: Colors.green,
onPrimary: Colors.white,
),
);
return Container();
})
],
),
);
}

If I understand you correclty, cant you just save items in WillPopScope like
return WillPopScope(
onWillPop: () async => SaveMyPreferences,
child: const Scaffold(
body: Container(
color: Colors.red,
size: 50.0,
),
),
);

I found a solution. If your data that you want to save comes from the API and is constantly updated (as it was in my case), then you do not need to use the shared preference package. This package will not help you. In my case, in order to save the checkboxes selected by the user and after reloading the page to show him which items in the list were selected (I use checkboxes), I write to a file on the device and then read the saved data from this file. So you are going to need path_provider package and dart:io and these two functions
to write from function where you choose items
_onCategorySelected(bool selected, id) async {
final Directory directory =
await getApplicationDocumentsDirectory();
if (selected == true) {
multipleSelected.add(id);
} else {
multipleSelected.remove(id);
}
final File file = File('${directory.path}/my_file.json');
file.writeAsStringSync('{"selected": $multipleSelected}');
setState(() {});
}
to read from file:
Future<String> read() async {
String text = '';
try {
final Directory directory =
await getApplicationDocumentsDirectory();
final File file = File('${directory.path}/my_file.json');
text = await file.readAsString();
print('HELLO');
multipleSelected = json.decode(text)["selected"];
} catch (e) {
print("Couldn't read file");
}
return text;
}
and before the listview.builder comes, you need to use read() function ro read the saved values from file.
It is not the greatest solution (maybe, the worst one), but if you haven't got enough time and you don't have any state management and you just need to solve issue right now, it can be really helpfull.

Related

dynamically created checkbox dart/flutter

I am trying to dynamically create some checkboxes based on data pulled from an API. My checkboxes are created but when I click on them they are all being checked or unchecked.
I am pretty sure I can identify why I am getting is, I am just unsure how to overcome it.
The variable I create is for one check box, so when my list is created from ListView.builder, it is using the same variable thereby making all boxes check and uncheck. I know that I need to create that variable based on how many items are in the list. I am just not sure how to do this and where within my code structure. I tried different methods of using .length or trying to use a .forEach but none of it was correct in the method I was implementing it. I have included my code that shows how I am creating my list of tags.
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:gateway_device/backend/api/api_services.dart';
import 'package:gateway_device/flutter_flow/flutter_flow_theme.dart';
import 'package:gateway_device/models/tag_list_model.dart';
class TagsListWidget extends StatefulWidget {
final int companyId;
const TagsListWidget({Key? key, required this.companyId}) : super(key: key);
#override
State<TagsListWidget> createState() => _TagsListWidgetState(companyId);
}
class _TagsListWidgetState extends State<TagsListWidget> {
final int companyId;
late bool checkboxListTileValue = false;
final scaffoldKey = GlobalKey<ScaffoldState>();
_TagsListWidgetState(this.companyId);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Tags",
textAlign: TextAlign.center,
style: FlutterFlowTheme.of(context).title2.override(
fontFamily: 'Heebo',
fontSize: 18.sp,
fontWeight: FontWeight.w500),
),
elevation: 0,
actions: <Widget>[
IconButton(
hoverColor: Colors.transparent,
iconSize: 40,
icon: Icon(
Icons.search,
color: Colors.black,
size: 20,
),
onPressed: () {
print("Test");
},
)
],
leading: IconButton(
hoverColor: Colors.transparent,
iconSize: 40,
icon: Icon(
Icons.keyboard_return_sharp,
color: Colors.black,
size: 30,
),
onPressed: () {
Navigator.pop(context);
},
),
centerTitle: true,
backgroundColor: Colors.white,
iconTheme: IconThemeData(color: Colors.black),
),
backgroundColor: Colors.white,
body: SafeArea(
child: FutureBuilder(
future: ApiService().getTagList(companyId),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
Tags tags = snapshot.data[index];
return GestureDetector(
onTap: (() {
FocusScope.of(context).unfocus();
}),
child: Center(
child: CheckboxListTile(
value: checkboxListTileValue,
onChanged: (newValue) => setState(() {
checkboxListTileValue = newValue!;
}),
title: Text(tags.tag,
textAlign: TextAlign.start,
style: FlutterFlowTheme.of(context)
.title3
.override(
fontFamily: 'Heebo',
color: Colors.black,
fontSize: 18.sp)),
),
),
);
});
}
return Center(
child: CircularProgressIndicator(),
);
}),
),
);
}
}
I appreciate the help!
You can create List:
List<bool> checkboxValue = [];
then use it like this:
return StatefulBuilder(// add this
builder: (c, innerSetState) {
return GestureDetector(
onTap: (() {
FocusScope.of(context).unfocus();
}),
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
Tags tags = snapshot.data[index];
checkboxValue = List<bool>.generate(snapshot.data.length,(counter) => false); // add this
return GestureDetector(
onTap: (() {
FocusScope.of(context).unfocus();
}),
child: Center(
child: CheckboxListTile(
value: checkboxValue[index], //add this
onChanged: (newValue) {
innerSetState(() {// add this
checkboxValue[index] = newValue;
});
},
title: Text(tags.tag,
textAlign: TextAlign.start,
style: FlutterFlowTheme.of(context)
.title3
.override(
fontFamily: 'Heebo',
color: Colors.black,
fontSize: 18.sp)),
),
),
);
}),
);
},
);
note that this work when checkbox value not come from api, if it is let me know so I can Update my answer.
Here is the problem, that you use varieble checkboxListTileValue in global scope. Then yo set new value in:
onChanged: (newValue) => setState(() {
checkboxListTileValue = newValue!;})
Global checkboxListTileValue applayed to all ListView items.
You can made the List checkboxListTileValue, with defaul value set. And onChage set the new value only for item with index which was clicked.
onChanged: (newValue) => setState(() {
checkboxListTileValue[index] = newValue!;})
You are using single bool to control the checked status of a list. You can create a model class with a bool filed that will be like bool isCheked=false , Once you click on item check it is already checked or not and change the value.
Or create a List that will hold tags.tag value.
List<String> selected = [];
....
value: selected.contains(tags.tag),
onChanged: (newValue)
{
if(selected.contains(tags.tag)){
selected.remove(tags.tag); }
else{
selected.add(tags.tag);
}
setState((){});

flutter: fetching folder names and storing them in a list from cloud storage

So I have managed to fetch the names of the folder and store them in a list. But the problem I am facing is is the list gets called twice so I tend to have duplicate items in my list view. I've asked similar questions about this but the solutions given don't work on my side.
I know the problem is me calling the getFolders() in the future(which is a bad practice) but that is the only way my code actually work. When I change my list to a type Future I can't use the .add() functionality.
Below is my code:
Here is where I have declared my list:
class Semester extends StatefulWidget {
final String value;
const Semester({Key? key, required this.value}) : super(key: key);
#override
State<Semester> createState() => _SemesterState();
}
class _SemesterState extends State<Semester> {
late List<String> courses = []; // possible culprit
Future<List<FirebaseFolder>>? listResult;
Future<List<String>> getFolders() async {
final storageRef = FirebaseStorage.instance.ref().child(widget.value);
final listResult = await storageRef.listAll();
for (var prefix in listResult.prefixes) {
courses.add(prefix.name);
}
return courses;
}
#override
void initState() {
// TODO: implement initState
super.initState();
getFolders();
}
So when I change the courses to Future<List> I can't use the courses.add(prefix.name) since it says it is not of type future:
And as you can see below I had to use the getFolder() function on my future for it to display contents on my listview, (NOTE: even if I use it on instantiating the result is still same:)
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
leading: kIconButton(),
elevation: 0,
centerTitle: true,
title: const Text("Semester", style: kTitleStyle),
backgroundColor: Colors.white,
),
body: FutureBuilder(
future: getFolders(), // possible culprit
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(
child: spinkitLines,
);
default:
return Column(
children: [
const SizedBox(
height: 20.0,
),
const Center(
child: Text(
'Tap to choose course',
style: kPlaceholderStyle,
),
),
const SizedBox(
height: 30.0,
),
Expanded(
child: ListView.builder(
itemCount: courses.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: GestureDetector(
onTap: () {
String passedValue = widget.value;
String course = courses[index];
String value = "$passedValue""/""$course";
Get.to(() => CourseContent(value: value, courseChosen: course,));
},
child: Container(
height: 80,
decoration: const BoxDecoration(
color: Colors.black,
borderRadius:
BorderRadius.all(Radius.circular(20)),
boxShadow: [
BoxShadow(
color: Color.fromARGB(75, 0, 0, 0),
blurRadius: 4,
spreadRadius: 0,
offset: Offset(0, 4))
],
),
child: Center(
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Text(
courses[index],
style: kCardTitleStyle,
),
),
),
),
),
);
}),
),
],
);
}
},
),
);
}
}
So I am looking for a way to fetch the list(folders prefixes) and display them in a list view. What I have tried above works but sometimes it displays duplicates which I can tell the getfolder() is being called multiple times.. Help me solve this.
So I came up with a clever and simple solution.
I wrapped my for loop with if to check if the array is empty. if not it won't run that code!
late List<String> courses = [];
Future<List<FirebaseFolder>>? listResult;
Future<List<String>> getFolders() async {
final storageRef = FirebaseStorage.instance.ref().child(widget.value);
final listResult = await storageRef.listAll();
if(courses.isEmpty){
for (var prefix in listResult.prefixes) {
courses.add(prefix.name);
}
}
return courses;
}
It is possible to make sure you get the data from snapshot like this way as shown on blow.
Note: elements in that child is an example.
if(!snapshot.hasData) {
return Center(
child: const CircularProgressIndicator(
backgroundColor: Colors.lightBlue,
),
);
}

I cannot update UI state with riverpod

I'm working on a quiz app as a personal project and what I want to do is make it possible for the user to name a question set. (Kind of like a folder for questions on a particular subject). I am using Riverpod. (I've worked with the provider package a couple of times) for state management but it seems I've missed a step or two because when I type in the name, I don't see it on the page. I hope I can be pointed in the right direction. Thanks
Class forRiverpod model which shows a list of type QuestionSetConstructor for taking the title. There is also a method for accepting the question title to add to the list
class RiverpodModel extends ChangeNotifier {
final List<QuestionSetConstructor> _questionSetList = [];
UnmodifiableListView<QuestionSetConstructor> get questionSet {
return UnmodifiableListView(_questionSetList);
}
void addTitleT(String title) {
final addQuestionTitle = (QuestionSetConstructor(title: title));
_questionSetList.add(addQuestionTitle);
notifyListeners();
}
int get count{
return questionSet.length;
}
}
`
This is for the alert dialog that will take the question title.
In the elevated button, I stated that I want the contents of the
text field to be added to the list in the Riverpod model.
void setQuestionNameMethod(context) {
showDialog(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return SetQuestionNameAlertDialog();
});
}
class SetQuestionNameAlertDialog extends ConsumerStatefulWidget {
#override
_SetQuestionNameAlertDialogState createState() =>
_SetQuestionNameAlertDialogState();
}
class _SetQuestionNameAlertDialogState
extends ConsumerState<SetQuestionNameAlertDialog> {
final TextEditingController questionNameController = TextEditingController();
final riverPodModelProvider2 =
ChangeNotifierProvider((ref) => RiverpodModel());
#override
Widget build(
BuildContext context,
) {
final questionNameRef = ref.watch(riverPodModelProvider2);
return AlertDialog(
title: Text("Name of Question Set",
style: TextStyle(
color: Colors.blue[400],
fontSize: 20,
fontWeight: FontWeight.w600)),
content: TextField(
controller: questionNameController,
),
actions: [
Center(
child: ElevatedButton(
onPressed: () {
setState(() {
questionNameRef.addTitleT(questionNameController.text) ;
});
print(questionNameRef.questionSet.first.title);
Navigator.pop(context);
},
child: const Text("Save"))),
],
);
}
}
`
This is the page where the question title is shown as a list. However, for some reason it is not showing.
class QuestionSetPage extends ConsumerStatefulWidget {
#override
_QuestionSetPageState createState() => _QuestionSetPageState();
}
class _QuestionSetPageState extends ConsumerState<QuestionSetPage> {
final riverPodModelProvider =
ChangeNotifierProvider((ref) => RiverpodModel());
#override
Widget build(BuildContext context) {
final questionSetRef = ref.watch(riverPodModelProvider);
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.grey[50],
centerTitle: true,
actions: [
IconButton(
onPressed: () {
setState(() {
setQuestionNameMethod(context);
// modalSheetMethod(context);
});
},
icon: Icon(
Icons.add,
size: 25,
color: Colors.blue[400],
))
],
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Question Set List",
style: TextStyle(
color: Colors.blue[400],
fontSize: 30,
fontWeight: FontWeight.w600),
),
),
Expanded(
child: ListView.builder(
itemCount: questionSetRef.count,
itemBuilder: (BuildContext context, int index) {
return Tiles(
title: questionSetRef.questionSet[index].title,
);
}),
)
],
),
);
}
}
class Tiles extends StatelessWidget {
String title;
Tiles({required this.title});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(15, 3, 15, 3),
child: Material(
elevation: 2,
child: GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return QuestionSetsQuestionPage();
}));
},
child: ListTile(
title: Text(
title,
style: TextStyle(
color: Colors.blue[400],
fontSize: 17,
fontWeight: FontWeight.w400),
),
tileColor: Colors.white,
// subtitle: Text(
// "${questionSets[index].numberOfQuestions} number of questions",
// ),
leading: const Icon(
Icons.add_box_outlined,
size: 30,
),
),
),
),
);
}
}

Data not updating in DB Sqflite Flutter

The task text in edit_todo_screen is not updated. I'm using the same code as in item_tasks, where I change the status of the task to move between "Done" and "Archived" - everything works well here. I tried to change only the status in edit_todo_screen, but it does not change, although the code is identical to the code in item_tasks. Perhaps the problem is that I'm not passing the parameters correctly to edit_todo_screen. I need to be able to change the status of the task and the text of the task itself in edit_todo_screen. Attached below is a screenshot of the error that occurs when clicking the button in edit_todo_screen
Tell me, please, what could be my mistake?
cubit_db
class AppCubit extends Cubit<AppStates> {
AppCubit() : super(AppInitialState());
static AppCubit get(context) => BlocProvider.of(context);
void updateDatabase(String status, int id) async {
database!.rawUpdate(
'UPDATE tasks SET status = ? WHERE id = ?', [status, id]).then((value) {
getDataBase(database);
emit(AppUpdateDatabaseState());
});
}
void createDatabase() {
openDatabase(
'todo.db',
version: 1,
onCreate: (database, version) {
database
.execute(
'CREATE TABLE tasks (id INTEGER PRIMARY KEY, title TEXT, status TEXT)')
.then((value) => print('Table Created'))
.catchError((error) {
print('Error When Creating Table ${error.toString()}');
});
},
onOpen: (database) {
getDataBase(database);
print('database opened');
},
).then((value) {
database = value;
emit(AppCreateDatabaseState());
});
}
inserToDatabase({required String title}) async {
await database!.transaction((txn) async {
txn
.rawInsert(
'INSERT INTO tasks (title, status) VALUES ("$title","New")')
.then((value) {
getDataBase(database);
print('$value Inserted Successfully');
emit(AppInsertDatabaseState());
}).catchError((error) {
print('Error When inserting Table ${error.toString()}');
});
});
}
new_tasks_list
class NewTasksScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocConsumer<AppCubit, AppStates>(
listener: (context, state) {},
builder: (context, state) {
var tasks = AppCubit.get(context).newTasks;
return SingleChildScrollView(
child: Column(children: [
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: tasks.length,
itemBuilder: (context, index) => TaskItem(tasks: tasks[index]),
),
]),
);
},
);
tasks_item
class TaskItem extends StatelessWidget {
Map? tasks;
TaskItem({this.tasks});
#override
Widget build(BuildContext context) {
return Card(
key: Key(tasks!['title']),
shadowColor: Colors.blueGrey,
margin: const EdgeInsets.only(left: 15, right: 15, top: 8),
color: Colors.black,
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.grey.shade800, width: 0.5),
borderRadius: BorderRadius.circular(10),
),
borderOnForeground: false,
child: ListTile(
title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(
// '${state.loadedUser[index].description}',
tasks!['title'],
style: const TextStyle(
fontSize: 21.0,
// fontWeight: FontWeight.bold,
),
),
// Text(
// tasks!['status'],
// style: const TextStyle(fontSize: 21.0),
// ),
]),
trailing: IconButton(
tooltip: 'Archive Todo',
highlightColor: Colors.red,
onPressed: () {
AppCubit.get(context).updateDatabase('Archive', tasks!['id']);
},
icon: const Icon(
Icons.archive,
color: Colors.white,
),
),
leading: IconButton(
tooltip: 'Done Todo',
highlightColor: Colors.green,
onPressed: () {
AppCubit.get(context).updateDatabase('Done', tasks!['id']);
},
icon: const Icon(
Icons.check,
color: Colors.white,
),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditTodoScreen(
title: tasks!['title'],
id: tasks!['id'],
),
),
);
},
),
);
}
}
edit_todo_screen
class EditTodoScreen extends StatelessWidget {
// Map? tasks;
String title;
int id;
EditTodoScreen({Key? key, required this.title, required this.id})
: super(key: key);
final _controller = TextEditingController();
#override
Widget build(BuildContext context) {
_controller.text = title;
return BlocConsumer<AppCubit, AppStates>(
listener: (context, state) {},
builder: (context, state) {
return Scaffold(
appBar: AppBar(
title: const Text(
'Edit Todo',
style: TextStyle(fontSize: 20.0),
),
),
body: _body(context),
);
});
}
Widget _body(context) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
TextFormField(
controller: _controller,
autocorrect: true,
maxLines: 2,
decoration: const InputDecoration(hintText: 'Enter todo message'),
),
const SizedBox(
height: 10.0,
),
// ElevatedButton(
// // style:,
// onPressed: () {
// AppCubit.get(context).updateDatabase('Done', id);
// },
// child: Text(
// 'Update Data',
// style: TextStyle(color: Colors.amber.shade700),
// ),
// ),
InkWell(
onTap: () {
AppCubit.get(context).updateDatabase('Done', id);
Navigator.pop(context);
},
child: _updateBtn(context),
)
],
),
);
}
Widget _updateBtn(context) {
return Container(
width: MediaQuery.of(context).size.width,
height: 50.0,
decoration: BoxDecoration(
color: Colors.black, borderRadius: BorderRadius.circular(10.0)),
child: Center(
child: Text(
'Update Todo',
style: TextStyle(
fontSize: 17.0,
color: Colors.amber.shade700,
fontWeight: FontWeight.bold),
),
),
);
}
}
I think your problem has to do with the fact that database is not set in the second case. The code fails because you try to access a null value that then is checked with the "!" operator. Look where you set the database and check if that code is called in both code flows.
edit:
I think this line in edit todo screen is your problem: AppCubit.get(context).updateDatabase('Done', id);. If I am not mistaken AppCubit.get(context) returns null. The easiest way to check if I am right would be to replace it with the following:
final appCubit = AppCubit.get(context);
print('$appCubit');
appCubit.updateDatabase('Done', id);
If I am right, you should see "null" in your terminal.
What I think happens is, that the app cubit is not provided in the context anymore, because you pushed the todo screen as a new screen on the navigator. With that, the context that is provided is that of the navigator (or probably the material app in your case) which is above the point where you provide the AppCubit.
I am kind of guessing though, because I only see half of your code. I hope it helps nevertheless. :)

How to pass DocumentSnapshot Id between two classes?

I have two dropdown menu's. One Represent the Category and Other Represent the Sub Category. The values should be retrieved from firesotore. Where the collection is created as a Nested Collection.
Categories & SubCategories collection are shown in the image
class SelectCategory extends StatefulWidget {
#override
_SelectCategoryState createState() => _SelectCategoryState();
}
class _SelectCategoryState extends State<SelectCategory> {
AdminDatabaseMethods adminDatabaseMethods = AdminDatabaseMethods();
var selectedCategory;
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: adminDatabaseMethods.getCategories(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
List<DropdownMenuItem> categoryMenu = [];
for (var i = 0; i < snapshot.data.docs.length; i++) {
DocumentSnapshot snap = snapshot.data.docs[i];
categoryMenu.add(DropdownMenuItem(
child: Text(snap.data()["Category_Name"]),
value: "${snap.id}",
));
//adminDatabaseMethods.getSubCategories(snap);
}
return DropdownButton(
value: selectedCategory,
icon: Icon(
Icons.arrow_downward_sharp,
color: Colors.amber,
),
iconSize: 20,
elevation: 16,
hint: Text(
"Select Main Categories",
style: TextStyle(color: Colors.amber),
),
style: TextStyle(color: Colors.amber),
underline: Container(
height: 2,
color: Colors.amber[300],
),
onChanged: (categoryValue) {
setState(() {
selectedCategory = categoryValue;
adminDatabaseMethods.getSubCategories(selectedCategory);
});
},
items: categoryMenu,
);
});
}
}
This is how I have created the list of Categoires and followed the same way to create the subcategories. What I require is I when i press the category I need to pass the documentsnapshot id to the other class.
Where,
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SizedBox(
width: 15,
),
SelectCategory(),
SizedBox(
width: 15,
),
SelectsubCategories(),
SizedBox(
width: 15,
),
],
),
Categories and Sub Categories are given like this. Is there a way of passing the data. As i am new to flutter please suggest me a method thanks.
I think what you're looking for is a state management solution. Learn more here.