[英]Flutter: getx controller not updated when list data changed. How can i make sure with every list added, GetX controller knows that?
[英]Flutter - getx controller not updated when data changed
我正在开发一个具有五页底部导航栏的应用程序。 我使用getx。 在第一页,我列出数据。 我的问题是,当我从数据库手动更改数据(底部导航栏中的第一页)并传递页面时,回到第一页我看不到更改。
Controller;
class ExploreController extends GetxController {
var isLoading = true.obs;
var articleList = List<ExploreModel>().obs;
@override
void onInit() {
fetchArticles();
super.onInit();
}
void fetchArticles() async {
try {
isLoading(true);
var articles = await ApiService.fetchArticles();
if (articles != null) {
//articleList.clear();
articleList.assignAll(articles);
}
} finally {
isLoading(false);
}
update();
}
}
和我的用户界面;
body: SafeArea(
child: Column(
children: <Widget>[
Header(),
Expanded(
child: GetX<ExploreController>(builder: (exploreController) {
if (exploreController.isLoading.value) {
return Center(
child: SpinKitChasingDots(
color: Colors.deepPurple[600], size: 40),
);
}
return ListView.separated(
padding: EdgeInsets.all(12),
itemCount: exploreController.articleList.length,
separatorBuilder: (BuildContext context, int index) {
GetX 不知道/看不到数据库数据何时更改/更新。
您需要告诉 GetX 在适当的时候重建。
如果您将 GetX observables
对象与GetX
或Obx
小部件一起使用,那么您只需为您的observable
字段分配一个新值。 当obs
值更改时,将发生重建。
如果您将 GetX 与GetBuilder<MyController>
一起使用,则需要在MyController
中调用update()
方法来重建GetBuilder<MyController>
小部件。
下面的解决方案使用 GetX Controller (即TabX
)来:
持有申请 state:
tabPages
)selectedIndex
) 公开一种更改活动/可见选项卡的方法( onItemTapped()
)
此方法位于TabX
内部,即 GetXController。
调用时,它将:
FakeDB
)update()
重建任何 GetBuilder 小部件 void onItemTapped(int index) {
selectedIndex = index;
db.insertViewedPage(index); // simulate database update while tabs change
update(); // ← rebuilds any GetBuilder<TabX> widget
}
将整个代码复制/粘贴到应用程序中的 dart 页面中,以查看工作的 BottomNavigationBar 页面。
这个选项卡式/BottomNavigationBar 示例取自https://api.flutter.dev/flutter/material/BottomNavigationBar-class.html 。但编辑为使用 GetX
import 'package:flutter/material.dart';
import 'package:get/get.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',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyTabHomePage(),
);
}
}
class FakeDB {
List<int> viewedPages = [0];
void insertViewedPage(int page) {
viewedPages.add(page);
}
}
/// BottomNavigationBar page converted to GetX. Original StatefulWidget version:
/// https://api.flutter.dev/flutter/material/BottomNavigationBar-class.html
class TabX extends GetxController {
TabX({this.db});
final FakeDB db;
int selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
List<Widget> tabPages;
@override
void onInit() {
super.onInit();
tabPages = <Widget>[
ListViewTab(db),
Text(
'Index 1: Business',
style: optionStyle,
),
Text(
'Index 2: School',
style: optionStyle,
),
];
}
/// INTERESTING PART HERE ↓ ************************************
void onItemTapped(int index) {
selectedIndex = index;
db.insertViewedPage(index); // simulate database update while tabs change
update(); // ← rebuilds any GetBuilder<TabX> widget
// ↑ update() is like setState() to anything inside a GetBuilder using *this*
// controller, i.e. GetBuilder<TabX>
// Other GetX controllers are not affected. e.g. GetBuilder<BlahX>, not affected
// by this update()
// Use async/await above if data writes are slow & must complete before updating widget.
// This example does not.
}
}
/// REBUILT when Tab Page changes, rebuilt by GetBuilder in MyTabHomePage
class ListViewTab extends StatelessWidget {
final FakeDB db;
ListViewTab(this.db);
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: db.viewedPages.length,
itemBuilder: (context, index) =>
ListTile(
title: Text('Page Viewed: ${db.viewedPages[index]}'),
),
);
}
}
class MyTabHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
Get.put(TabX(db: FakeDB()));
return Scaffold(
appBar: AppBar(
title: const Text('BottomNavigationBar Sample'),
),
body: Center(
/// ↓ Tab Page currently visible - rebuilt by GetBuilder when
/// ↓ TabX.onItemTapped() called
child: GetBuilder<TabX>(
builder: (tx) => tx.tabPages.elementAt(tx.selectedIndex)
),
),
/// ↓ BottomNavBar's highlighted/active item, rebuilt by GetBuilder when
/// ↓ TabX.onItemTapped() called
bottomNavigationBar: GetBuilder<TabX>(
builder: (tx) => BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
currentIndex: tx.selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: tx.onItemTapped,
),
),
);
}
}
感谢@Baker 的正确答案。 但是,如果您有一个列表并且在 viewModel 中并且想要更新该列表,只需在列表更新时使用list.refresh()
RxList<Models> myList = <Models>[].obs;
添加或插入数据时的行为如下:
myList.add(newItem);
myList.refresh();
GetX< ExploreController >(builder: (controller) {
if (controller.isLoading.value) {
return Center(
child: SpinKitChasingDots(
color: Colors.deepPurple[600], size: 40),);
}
return ListView.separated(
padding: EdgeInsets.all(12),
itemCount: controller.articleList.length,
separatorBuilder: (BuildContext context, int index) {});
});
您在这里不需要 GetBuilder,因为它不适用于可观察变量。 您也不需要在 fetchArticles function 中调用 update(),因为这仅适用于 GetBuilder 和不可观察变量。
因此,您有 2 个用于更新 UI 的小部件(GetBuilder 和 Obx),它们都遵循相同的 controller,而您只需要 OBX。 因此,Rahuls 回答有效,或者您可以将 Obx 留在原处,摆脱 GetBuilder 并在构建方法的开头声明并初始化 controller。
final exploreController = Get.put(ExploreController());
然后在 OBX 小部件中使用初始化的 controller 作为 Expanded 的子级。
Obx(() => exploreController.isLoading.value
? Center(
child:
SpinKitChasingDots(color: Colors.deepPurple[600], size: 40),
)
: ListView.separated(
padding: EdgeInsets.all(12),
itemCount: exploreController.articleList.length,
separatorBuilder: (BuildContext context, int index) {},
),
)
如果您“手动”更改数据库中的值,则需要 STREAM 来监听数据库的更改。 你不能这样做:
var articles = await ApiService.fetchArticles();
你需要做这样的事情:
var articles = await ApiService.listenToArticlesSnapshot();
您解释的方式就像您需要在导航到另一个页面并单击按钮后刷新数据,然后导航到第一页(GetBuilder)或自动从第一页(Obx)中添加数据。 但是你的情况很简单,只需检索文章 SNAPSHOT,然后在 controller onInit 中,使用 bindStream 方法订阅快照,最终使用 function ever() 对可观察文章列表中的任何更改做出反应。 像这样的东西:
创建最终 exploreController = Get.put(ExploreController());
添加初始化:ExploreController();
body: SafeArea(
child: Column(
children: <Widget>[
Header(),
Expanded(
child: GetX<ExploreController>(builder: (exploreController) {
*** here ***
init: ExploreController();
if (exploreController.isLoading.value) {
return Center(
child: SpinKitChasingDots(
color: Colors.deepPurple[600], size: 40),
);
}
return ListView.separated(
padding: EdgeInsets.all(12),
itemCount: exploreController.articleList.length,
separatorBuilder: (BuildContext context, int index) {
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.