首页 > 解决方案 > 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,
          )
        )
        )
        );
        }
    );
          },
        )
    );
  }
}

标签: buttonflutterfuture

解决方案


原因
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,
                          ))));
                });
              }
            }));
  }
}

推荐阅读