如何更新导航抽屉中的有状态小部件,同时在 Android 中保持与片段相同的类?
Posted
技术标签:
【中文标题】如何更新导航抽屉中的有状态小部件,同时在 Android 中保持与片段相同的类?【英文标题】:How to update stateful widget in Navigation drawer while keeping same class like fragments in Android? 【发布时间】:2018-11-17 14:06:01 【问题描述】:我想在从服务器、导航抽屉获取数据后返回同一个类的同时更新我的类的有状态小部件。我遇到的问题是,如果我导航到导航抽屉的另一个项目,类只加载一次数据并且保持不变。因为状态只创建一次。
这是我的代码:
class CategoryFilter extends StatefulWidget
int productIndex;
String category_name;
CategoryFilter(this.productIndex, this.category_name)
print("CategoryFilter");
print(productIndex);
print(category_name);
new _CategoryFilterState(productIndex, category_name);
@override
_CategoryFilterState createState() => new
_CategoryFilterState(productIndex, category_name);
class _CategoryFilterState extends State<CategoryFilter>
int productIndex;
List<ResponseDataProducts> productList;
List data;
String category_name;
_CategoryFilterState(this.productIndex, this.category_name)
print("CategoryFilter");
print(productIndex);
print(category_name);
@override
void initState()
super.initState();
Future<String> status = getData(productIndex);
status.then((onValue)
if(onValue.toString() == "Success")
Navigator.pop(context);
);
// this.getData();
@override
Widget build(BuildContext context)
return new Scaffold(
body: new Container(
color: Colors.white30,
child: new ListView.builder(
itemCount: productList == null ? 0 : productList.length,
itemBuilder: (BuildContext context, int index)
return new Container(
margin: const EdgeInsets.only( bottom: 10.0),
constraints: new BoxConstraints.expand(
height: 200.0
),
alignment: Alignment.bottomLeft,
decoration: new BoxDecoration(
image: new DecorationImage(image:
new NetworkImage
("http://myurl.com/"+productList[index].thumbnail),
fit: BoxFit.cover)
),
child:new Container(
child: new Text(
productList[index].name,
style: new TextStyle(color: Colors.white, fontSize: 30.0),
),
color: Colors.black54,
alignment: new FractionalOffset(0.5, 0.0),
height: 35.0,
// margin: const EdgeInsets.symmetric(vertical: 30.0),
),
);
)
),
) ;
void _onLoading()
showDialog(context: context,
barrierDismissible: false,
child: progress);
new Future.delayed(new Duration(seconds: 2), ()
// Navigator.pop(context);
);
Future<String> getData(int productIndex) async
productList = new List<ResponseDataProducts>();
_onLoading();
http.Response response = await http.get(
Uri.encodeFull(CommonMethods.base_url + 'product/$productIndex'),
headers: "Accept": "application/json");
print(response.body);
setState(()
var convertDataToJson = JSON.decode(response.body);
data = convertDataToJson["responseData"];
for(int i=0; i<data.length; i++)
ResponseDataProducts responseData = new ResponseDataProducts(
data[i]["id"],
data[i]["name"], data[i]["description"],
data[i]["title"], data[i]["thumbnail"]);
productList.add(responseData);
//Navigator.pop(context);
);
return "Success";
这是我从导航抽屉中调用这个 categoryFilter 类的方式:
_getDraserItemWidget(int pos)
switch(pos)
case 0:
return new Home(bar_id);
case 1:
return new CategoryFilter(categoryList[pos-1].id, categoryList[pos-1].name);
case 2:
return new CategoryFilter(categoryList[pos-1].id, categoryList[pos-1].name);
case 3:
return new CategoryFilter(categoryList[pos-1].id, categoryList[pos-1].name);
case 4:
return new OpeningTime();
case 5:
break;
【问题讨论】:
【参考方案1】:我建议您使用FutureBuilder
小部件,而不是调用该方法在您的类的initState
方法中加载数据。如果您从导航抽屉返回一个新的FutureBuilder
,则每次创建一个新的时都会调用您的服务,并且通常是执行异步请求的更好方法。
这是一个非常简单的例子。它不能很好地完成抽屉(或其他一些事情 - 只有这么多时间可以花在这样的事情上),但它应该说明这个概念。
请注意,它不是“更新小部件”,而是简单地创建一个新小部件。由于 Flutter 做事的方式,这应该是相对高效的,尤其是因为您不是一直都在这样做,而是仅在用户从导航菜单中选择某些内容时才这样做。
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget
@override
State<StatefulWidget> createState() => new MyAppState();
class MyAppState extends State<MyApp>
@override
Widget build(BuildContext context)
return new MaterialApp(
home: new TextPage(text: "Home!"),
);
Map<int, int> _nums = Map();
class TextPage extends StatelessWidget
final String text;
const TextPage(Key key, @required this.text) : super(key: key);
@override
Widget build(BuildContext context)
return new Scaffold(
appBar: new PreferredSize(
child: new Container(),
preferredSize: Size.fromHeight(10.0),
),
body: new Center(
child: new Text(text),
),
drawer: new Builder(
builder: (context) => Material(
child: new SafeArea(
child: Column(
children: <Widget>[
new FlatButton(
onPressed: ()
Navigator.pushReplacement(
context, new MaterialPageRoute(builder: (context) => _getDrawerItemWidget(1)));
,
child: Text("First item"),
),
new FlatButton(
onPressed: ()
Navigator.pushReplacement(
context, new MaterialPageRoute(builder: (context) => _getDrawerItemWidget(2)));
,
child: Text("Second item"),
),
],
),
),
),
),
);
_getDrawerItemWidget(int i)
return new FutureBuilder<String>(
builder: (context, AsyncSnapshot<String> snapshot)
if (snapshot.data != null)
return new TextPage(text: snapshot.data);
else
return new TextPage(text: "Loading.");
,
future: () async
var num = _nums.putIfAbsent(i, () => 0);
_nums[i] = num + 1;
String toReturn = "You retrieved number $i for the $num time";
return await Future.delayed<String>(Duration(seconds: 1), () => toReturn);
(),
);
理论上,您可以做一些不同的事情,保留 GlobalKey 引用并使用这些引用来调用子小部件上的方法(如果它与当前选择匹配以使其更新),但这在颤振中通常是一个坏主意 - 最佳实践鼓励您通过数据在小部件树中向下而不是向下调用函数。如果你必须使用 GlobalKeys,你通常可以重构来做一些更好的事情。
【讨论】:
谢谢@rmtmckenzie。但这又不能解决我在从导航类第二次调用同一类时更新小部件的问题。 当然可以。由于每次在导航抽屉中执行任何操作时都会创建一个新的未来,因此它会再次进行检索并使用结果构建一个新的小部件。新的小部件将包含更新的数据。另外,您还可以获得能够在加载时定义您想要发生的事情的优势(理论上,如果您愿意,您可以在此之前显示旧版本。我将通过一个简单的示例更新我的答案。此外,您的代码对于试图帮助你的人来说,包含实际上并不是很有帮助 - 你最好包含导航抽屉代码。以上是关于如何更新导航抽屉中的有状态小部件,同时在 Android 中保持与片段相同的类?的主要内容,如果未能解决你的问题,请参考以下文章