Flutter:“未处理的异常:在构建过程中调用了setState()或markNeedsBuild()”。尝试在当前路线后面的页面上调用setState()时

时间:2020-06-29 21:13:35

标签: flutter dart

我有一个有趣的设置。

我有一个扩展State并提供一些额外功能的类。在此类中,我有一个isLoading变量,该变量使我可以在此状态的任何实例中显示一个CircularProgressIndicator

我有一个类,该类向服务器执行API请求以获取数据,同时它使用控制器类在任何适用的小部件中将isLoading设置为true,以表明它正在加载数据。

问题是,当我加载新页面,然后API处理程序更新isLoading参数并且ConvenienceState类运行setState()时,我得到了现在隐藏的错误页,但我不确定为什么会这样。当前路线后面的页面是否必须以不方便的方式进行重建?我该如何解决这个问题?

这是一个有效的错误示例:

import 'dart:async';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Bug example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Bug Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  State<StatefulWidget> createState() {
    return MyHomePageState();
  }
}

class MyHomePageState extends ConvenienceState<MyHomePage> {

  MyHomePageState(){
    API.getData();
  }

// It doesn't work when API.getData() is here instead, either
//  @override
//  void initState() {
//    super.initState();
//    API.getData();
//  }


  @override
  Widget buildContent(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Center(
          child: RaisedButton(
            onPressed: (){
              Navigator.of(context).push(
                MaterialPageRoute(
                  builder: (context){
                    return NextPage();
                  }
                )
              );
            },
            child: Text("Next Page"),
          ),
        ),
      ),
    );
  }
}

class NextPage extends StatefulWidget{
  @override
  State<StatefulWidget> createState() {
    return NextPageState();
  }

}

class NextPageState extends ConvenienceState<NextPage>{
  NextPageState(){
    API.getData();
  }

// It doesn't work when API.getData() is here instead, either
//  @override
//  void initState() {
//    super.initState();
//    API.getData();
//  }

  @override
  Widget buildContent(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("NextPage"),),
      body: Center(child: Text("Content"),),
    );
  }

}

class API {
  static Future<bool> getData()async{
    RemoteController.update("loading");
    return await Future<bool>.delayed(Duration(seconds: 3),(){
      RemoteController.update("notLoading");
      return true;
    });
  }
}

class RemoteController {
  static List<Function(String)> listeners = [];
  static void update(String type) {
    listeners.forEach((Function listener) {
      listener(type);
    });
  }

  static void addListener(Function(String) listener) {
    listeners.add(listener);
  }

  static void removeListener(Function(String) listener) {
    listeners.remove(listener);
  }
}

abstract class ConvenienceState<T extends StatefulWidget> extends State{
  T get widget {
    return super.widget;
  }
  /// Keeps track of whether or not the page is built (ready for [setState()])
  bool isBuilt = false;

  bool _isLoading = false;

  /// Determines if the loading screen is displayed
  set isLoading(bool value){
    _isLoading = value;
    if(mounted && isBuilt)
      setState(() {});
  }
  /// Determines if the loading screen is displayed
  get isLoading{
    return _isLoading;
  }

  @override
  initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_){
      isBuilt = true;
    });

    RemoteController.addListener(rcListener);
  }
  void rcListener(String type) {
    if(type=="loading"){
      isLoading = true;
    }
    if(type=="notLoading"){
      isLoading = false;
    }
  }

  @override
  dispose(){
    RemoteController.removeListener(rcListener);
    super.dispose();
  }

  Widget buildContent(BuildContext context);

  @override
  build(BuildContext context){
    return Stack(
      children: <Widget>[
        buildContent(context),
        Visibility(
          visible: isLoading,
          child: Center(child: CircularProgressIndicator()),
        ),
      ],
    );
  }
}

当您单击“ NextPage”按钮时,将显示错误,涉及到MyHomePage类。

0 个答案:

没有答案