button - Flutter:显示未来的 MaterialButton 列表
问题描述
我正在写一个颤振的应用程序。我目前的问题是我想显示一个按钮列表来从网站打开不同的 pdf 文件。按钮列表是使用网络抓取创建的。该函数返回一个未来的字符串列表。从这个字符串列表创建一个未来的材质按钮列表。
因为网页抓取需要很长时间,所以应该在按钮列表尚未准备好时显示 LoadingDialog。之后,应显示按钮列表。
使用我当前的代码,按钮列表被创建了两次。第一次创建它并显示一个 LoadingDialog。然后 LoadingDialog 被隐藏,屏幕为白色,按钮列表被再次创建,当它准备好时,它显示在屏幕上。
如何防止列表重新形成?
这是我的代码:
class PlanScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AdvancedAppBar(context: context),
body: PlanForm(),
);
}
}
class PlanForm extends StatefulWidget {
@override
PlanState createState() {
return PlanState();
}
}
class PlanState extends State<PlanForm> {
String pathPDF;
List<MaterialButton> _buttonsList = [];
bool _listVariable = false;
Future<List<String>> doed()async {
// doed returns a Future<List<String>> with file names
}
@override
void initState() {
createFileOfPdfUrl("File-URL").then((f){setState((){pathPDF = f.path;});});
super.initState();
}
Future<List<Widget>> _buildButtonsWithNames() async{
List<String> list = [];
await doed().then((value){list = value;});
int length = list.length;
print("Länge der List " + length.toString());
_buttonsList = [];
for (int i = 0; i < length; i++) {
List<String> linkSplitted = list[0].split("/");
String buttonName = linkSplitted[linkSplitted.length-1];
_buttonsList
.add(new MaterialButton(
height: 40.0,
minWidth: 300.0,
color: phwtblue,
textColor: Colors.white,
child: new Text(buttonName, textScaleFactor: 2.0,),
onPressed: () {
bool hi = true;
if(buttonName.contains(".html"))
{
Navigator.push(context, new MaterialPageRoute(builder: (context) => new MyApp2()),
);
} else if (buttonName.contains(".pdf"))
{
Navigator.push(context, new MaterialPageRoute(builder: (context) => new PDFView(pathPDF)),
);
}
}
));
print(list.length);
list.removeAt(0);
}
print(_buttonsList.length);
return _buttonsList;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<List<Widget>>(
future: _buildButtonsWithNames().then((list){_buttonsList = list;}),
builder: (BuildContext context, AsyncSnapshot<List<Widget>> snapshot) {
while(snapshot.connectionState == ConnectionState.waiting){
showLoadingDialog();
return Column();
}
if(snapshot.connectionState == ConnectionState.done){
hideLoadingDialog();
_listVariable = false;
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: _buttonsList,
)
)
)
);
}
);
},
)
);
}
}
解决方案
原因
https://github.com/flutter/flutter/issues/11426#issuecomment-414047398
每次发布重建时都会调用 FutureBuilder 状态的 didUpdateWidget。此函数检查旧的未来对象是否与新的不同,如果是,则重新触发 FutureBuilder。为了解决这个问题,我们可以在构建函数之外的其他地方调用 Future。
解决方案
https://github.com/flutter/flutter/issues/11426#issuecomment-414047398
代码片段
Future<List<Widget>> Function() _future;
...
@override
void initState() {
//createFileOfPdfUrl("File-URL").then((f){setState((){pathPDF = f.path;});});
super.initState();
_future = _buildButtonsWithNames;
}
...
FutureBuilder<List<Widget>>(
future: _future().then((list) {
_buttonsList = list;
}),
完整的测试代码
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,
),
home: PlanScreen(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class PlanScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("test"),
),
body: PlanForm(),
);
}
}
class PlanForm extends StatefulWidget {
@override
PlanState createState() {
return PlanState();
}
}
class PlanState extends State<PlanForm> {
String pathPDF;
List<MaterialButton> _buttonsList = [];
bool _listVariable = false;
Future<List<String>> doed() async {
// doed returns a Future<List<String>> with file names
await Future.delayed(const Duration(seconds: 3), () {});
return ["123", "456", "abc", "def"];
/*setState(() {
return ["123", "456", "abc", "def"];
});*/
}
Future<List<Widget>> Function() _future;
@override
void initState() {
//createFileOfPdfUrl("File-URL").then((f){setState((){pathPDF = f.path;});});
super.initState();
_future = _buildButtonsWithNames;
}
Future<List<Widget>> _buildButtonsWithNames() async {
print("_buildButtonsWithNames()");
List<String> list = [];
await doed().then((value) {
list = value;
});
int length = list.length;
print("Länge der List " + length.toString());
_buttonsList = [];
for (int i = 0; i < length; i++) {
List<String> linkSplitted = list[0].split("/");
String buttonName = linkSplitted[linkSplitted.length - 1];
_buttonsList.add(new MaterialButton(
height: 40.0,
minWidth: 300.0,
color: Colors.blue,
textColor: Colors.white,
child: new Text(
buttonName,
textScaleFactor: 2.0,
),
onPressed: () {
bool hi = true;
if (buttonName.contains(".html")) {
//Navigator.push(context, new MaterialPageRoute(builder: (context) => new MyApp2()),
//);
} else if (buttonName.contains(".pdf")) {
//Navigator.push(context, new MaterialPageRoute(builder: (context) => new PDFView(pathPDF)),
//);
}
}));
print(list.length);
list.removeAt(0);
}
print(_buttonsList.length);
return _buttonsList;
}
@override
Widget build(BuildContext context) {
print("build");
return Scaffold(
body: FutureBuilder<List<Widget>>(
future: _future().then((list) {
_buttonsList = list;
}),
builder:
(BuildContext context, AsyncSnapshot<List<Widget>> snapshot) {
while (snapshot.connectionState == ConnectionState.waiting) {
//showLoadingDialog();
print("show loading");
return Column();
}
if (snapshot.connectionState == ConnectionState.done) {
//hideLoadingDialog();
print("hide loading");
_listVariable = false;
return LayoutBuilder(builder:
(BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: _buttonsList,
))));
});
}
}));
}
}
推荐阅读
- java - Log4j 编程配置 DefaultRolloverStrategy
- sql - 使用 DATEADD 更新 SQL 中的日期
- tensorflow2.0 - 有什么方法可以将模型从 tensorflow 对象检测 api 转换为 keras 模型
- reactjs - 反应原生刷新屏幕
- javascript - Javascript语音识别触发按钮
- google-fit - 获取少数数据类型的消息作为 googlefit 的“未找到默认数据源”
- c# - Unity tilemap 生成的图块和碰撞问题
- java - 如何在 GUI 应用程序中指定控制器?
- python - 识别重复值并将唯一字符串附加到它们
- chart.js - 条形的水平重叠,而不是整个图