#firebase #flutter #google-cloud-firestore #navigation #state-management
#firebase #flutter #google-облако-firestore #навигация #управление состоянием
Вопрос:
У меня есть простая задача. В моем приложении есть 2 экрана, на главном экране у меня есть кнопка, которая перенаправляет меня на новую страницу под названием «Интересы». Страница интересов представляет собой список флажков (для которых я должен использовать только listview.builder) и кнопку для отправки данных (которая возвращает меня к главному экрану). Чего я хочу добиться, так это:
- флажки должны работать правильно.
- когда я перехожу со страницы интересов на главную страницу и снова возвращаюсь на страницу интересов, выбранные флажки должны оставаться отмеченными. Короче говоря, состояние страницы должно быть сохранено.
- Я написал функцию «applyInterestChanges» для сохранения данных в базе данных. Я должен получить те же данные, чтобы отобразить выбранные флажки (что мы делали, передавая данные через конструктор).
Любая помощь будет оценена!!
import 'package:flutter/material.dart';
void main() {
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,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: RaisedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Interests(),
),
);
},
child: Text("Click here!!"),
),
),
),
);
}
}
class Interests extends StatefulWidget {
final List<dynamic> selectedList;
final void Function(List<dynamic>) callback;
Interests(this.selectedList, this.callback);
@override
_InterestsState createState() => _InterestsState();
}
class _InterestsState extends State<Interests> {
Map<String, dynamic> _categories = {
"responseCode": "1",
"responseText": "List categories.",
"responseBody": [
{"category_id": "1", "category_name": "Movies"},
{"category_id": "2", "category_name": "Sports"},
{"category_id": "3", "category_name": "Food"},
{"category_id": "4", "category_name": "Music"},
{"category_id": "5", "category_name": "Others"},
],
"responseTotalResult": 5
};
void _onCategorySelected(bool selected, categoryName) {
if (selected == true) {
setState(() {
widget.selectedList.add(categoryName);
});
} else {
setState(() {
widget.selectedList.remove(categoryName);
});
}
widget.callback(widget.selectedList);
}
applyInterestChanges() { //function to save the changes in database.
Firestore.instance
.collection('my_users')
.document(currentUserModel.id)
.updateData({
"interests": widget.selectedList,
});
} //this code is working properly. Need to similar function to retrieve the data and display the updated interests list.
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Interests"),
),
body: SingleChildScrollView(
child: Column(
children: [
Text(
"Select your interests: ",
style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),
),
ListView.builder(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: _categories['responseTotalResult'],
itemBuilder: (BuildContext context, int index) {
return CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
value: widget.selectedList.contains(
_categories['responseBody'][index]['category_name']),
onChanged: (bool selected) {
_onCategorySelected(selected,
_categories['responseBody'][index]['category_name']);
},
title:
Text(_categories['responseBody'][index]['category_name']),
);
},
),
MaterialButton(
onPressed: () {
Navigator.pop(context);
applyInterestChanges();
},
child: Text("Submit"),
),
],
),
),
);
}
}
Ответ №1:
Вы можете передать пустой список из родительского виджета MyHomeWidget
и обновить этот список с помощью обратного вызова из Interests
виджета.
В следующий раз, когда вы вернетесь и снова перейдете к Interests
виджету, мы передадим этот обновленный список, который сохранит состояние Interests
виджета. Следовательно, флажки будут установлены в зависимости от их значений в списке.
Вот реализация:
import 'package:flutter/material.dart';
void main() {
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,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<dynamic> selectedList = [];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Center(
child: RaisedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Interests(
selectedList,
(List<dynamic> updatedList) {
setState(() {
selectedList = updatedList;
});
}
),
),
);
},
child: Text("Click here!!"),
),
),
),
);
}
}
class Interests extends StatefulWidget {
Interests(this.selectedList, this.callback);
// Passing the list from parent widget i.e, MyHomeWidget
// Initially the list will be empty
// We will update the list in parent whenever checkboxes change
final List<dynamic> selectedList;
// Creating a callback function to save state(update list) in
// MyHomeWidget
final void Function(List<dynamic>) callback;
@override
_InterestsState createState() => _InterestsState();
}
class _InterestsState extends State<Interests> {
Map<String, dynamic> _categories = {
"responseCode": "1",
"responseText": "List categories.",
"responseBody": [
{"category_id": "1", "category_name": "Movies"},
{"category_id": "2", "category_name": "Sports"},
{"category_id": "3", "category_name": "Food"},
{"category_id": "4", "category_name": "Music"},
{"category_id": "5", "category_name": "Others"},
],
"responseTotalResult": 5
};
void _onCategorySelected(bool selected, categoryId) {
if (selected == true) {
setState(() {
widget.selectedList.add(categoryId);
});
} else {
setState(() {
widget.selectedList.remove(categoryId);
});
}
// Callback to save the updated selectedList to MyHomeWidget list
widget.callback(widget.selectedList);
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Interests"),
),
body: SingleChildScrollView(
child: Column(
children: [
Text(
"Select your interests: ",
style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),
),
ListView.builder(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: _categories['responseTotalResult'],
itemBuilder: (BuildContext context, int index) {
return CheckboxListTile(
value: widget.selectedList.contains(
_categories['responseBody'][index]['category_id']),
onChanged: (bool selected) {
_onCategorySelected(selected,
_categories['responseBody'][index]['category_id']);
},
title:
Text(_categories['responseBody'][index]['category_name']),
);
},
),
RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Go back!!"),
),
],
),
),
);
}
}
Вот метод, из которого вы хотели извлечь Firebase
. Я использовал обновленный класс FirebaseFirestore
. Если вы используете более старую версию Firebase
, просто замените FirebaseFirestore
на Firebase
.
Future<void> fetchInterestChanges() async { //function to get the changes in database.
final DocumentSnapshot doc = await FirebaseFirestore.instance
.collection('my_users')
.document(currentUserModel.id)
.get();
final updatedList = doc.data();
print(updatedList);
}
Комментарии:
1. Спасибо. Это решило задачу. Хитрость здесь в том, чтобы сохранить список в MyHomeWidget, используя функцию обратного вызова и передавая список из родительского виджета. Использование поставщика также послужило бы цели, верно? и какой из них является лучшим вариантом, учитывая этот сценарий .. setstate или Provider?
2. Да, вы
Provider
также можете использовать. Но для небольшого количества виджетов, как в вашем случае, я думаюStatefulWidget
, будет работать нормально.Provider
следует использовать, когда вам нужно получить доступ или передать данные между виджетами глубокой иерархии, где передача аргументов в конструкторе действительно утомительна, повторяющаяся и грязная.3. Понял! У меня есть другой запрос. Вышеупомянутый код является лишь частью более крупного приложения. Я должен сохранить выбранные интересы в базе данных firestore и использовать firestore для отображения выбранных интересов (что мы в настоящее время делаем, передавая данные через конструктор). Можете ли вы также поделиться тем, как мы можем этого добиться? Я просто обновляю код в вопросе
4. я обновил код и добавил комментарии, в которых я написал функцию. Если бы вы могли мне помочь с этим?
5. Вам просто нужно вызвать
get
метод вместоupdate
того, чтобы извлекать данныеFirebase
и анализировать их. Кроме того, я думаю, что вы используете более старую версиюFirebase
as now, вы должны использоватьFirebaseFirestore.instance
вместоFirestore.instance
.
Ответ №2:
Вы должны смешивать с классом AutomaticKeepAliveClientMixin
, чтобы сохранить старые виджеты, например: https://github.com/diegoveloper/flutter-samples/blob/master/lib/persistent_tabbar/page2.dart .
Или когда не работает:
- Вы должны сохранить флажок data в кеш, например, в общих присутствиях и т. Д.
- При повторном открытии страницы вы вызываете данные из кэша
- И когда установлен флажок / не сохранять данные в кеш