即使在编码使用不同的变量时,也使用单个实例的Widget类

问题描述 投票:0回答:3

我想使用Flutter的BottomNavigationBar,因此我创建了一个名为BaseWidget的类,当用户点击该项时,该类将被更改。

class BaseWidget extends StatefulWidget {
  final String title;

  BaseWidget(this.title);

  _BaseWidgetState createState() => _BaseWidgetState(this.title);
}

class _BaseWidgetState extends State<BaseWidget> {
  final String title;

  _BaseWidgetState(this.title);

  @override
  Widget build(BuildContext context) {
    return Center(child: Text(title));
  }
}

在上面的类中,我将Center小部件作为Text小部件返回给孩子。

class HomeWidget extends StatefulWidget {
  _HomeWidgetState createState() => _HomeWidgetState();
}

class _HomeWidgetState extends State<HomeWidget> {
  int pageIndex = 0;

  final _home = BaseWidget('Home');
  final _business = BaseWidget('Business');
  final _school = BaseWidget('School');

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Bottom Navigation Bar'),
        ),
        body: choosePager(),
        bottomNavigationBar: BottomNavigationBar(
          currentIndex: pageIndex,
          items: <BottomNavigationBarItem>[
            BottomNavigationBarItem(
                icon: Icon(Icons.home), title: Text('Home')),
            BottomNavigationBarItem(
                icon: Icon(Icons.business), title: Text('Business')),
            BottomNavigationBarItem(
                icon: Icon(Icons.school), title: Text('School')),
          ],
          onTap: onTap,
        ),
      ),
    );
  }

  void onTap(int index) {
    setState(() {
      this.pageIndex = index;
    });
  }

  Widget choosePager() {
    switch (pageIndex) {
      case 0:
        return _home;
        break;
      case 1:
        return _business;
        break;
      case 2:
        return _school;
        break;
      default:
        return Text('Unknown');
        break;
    }
  }
}

问题1:

每当用户点击BottomNavigationBarItem时,文本应该更改为在BaseWidget's构造函数中传递的受尊重的字符串。但它只显示Home,其余2被忽略。

问题2:

我计划用Center小部件替换ListView小部件,以填充将以分页方式从网络API获取的学校和企业列表。因此,当我点击BottomNavigationBarItem时,我不想再次重新初始化类,因为这会导致已经获取的数据丢失。为了防止数据丢失我宣布_home_business_school属性并在choosePager()方法中使用这些属性。

dart flutter
3个回答
1
投票

您的代码有几个问题:

1-真正的问题是你永远不会重建BaseWidget。你构造了3个新的BaseWidgets,但是你只调用了build小部件的_home,因为它是choosePager()返回的第一个。既然你没有在_home _business创建_schoolHomeWidgetbuild,那么其他任何BaseWidget都无法建造。

2-当您不需要为窗口小部件存储任何状态/变量时,请使用Stateless窗口小部件。

3-不要在你所在州的建设者中做任何事情。改为使用initState https://docs.flutter.io/flutter/widgets/State/initState.html

4-尽可能使用const构造函数创建小部件。

5- Widget构造函数获取命名参数。其中一个应该是关键。使用super来调用基础构造函数。

考虑到这一点,这就是代码应该是这样的:


class BaseWidget extends StatelessWidget {
  final String title;

  const BaseWidget({Key key, this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(title),
    );
  }
}

class HomeWidget extends StatefulWidget {
  _HomeWidgetState createState() => _HomeWidgetState();
}

class _HomeWidgetState extends State<HomeWidget> {
  int pageIndex = 0;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Bottom Navigation Bar'),
        ),
        body: choosePager(),
        bottomNavigationBar: BottomNavigationBar(
          currentIndex: pageIndex,
          items: <BottomNavigationBarItem>[
            BottomNavigationBarItem(
              icon: Icon(Icons.home),
              title: Text('Home'),
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.business),
              title: Text('Business'),
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.school),
              title: Text('School'),
            ),
          ],
          onTap: onTap,
        ),
      ),
    );
  }

  void onTap(int index) {
    setState(() {
      pageIndex = index;
    });
  }

  Widget choosePager() {
    Widget result;

    switch (pageIndex) {
      case 0:
        result = BaseWidget(title: 'Home');
        break;
      case 1:
        result = BaseWidget(title: 'Business');
        break;
      case 2:
        result = BaseWidget(title: 'School');
        break;
      default:
        result = Text('Unknown');
        break;
    }
    return result;
  }
}

编辑:对于您的示例,您可能希望从网络中获取一些数据,并仅使用小部件来显示它。在这种情况下,创建一个新类(不是Widget)来获取和保留数据,并仅使用Widget来显示数据。

一些示例代码:

/// Silly class to fetch data
class DataClass {
  static int _nextDatum = 0;
  int _data;

  DataClass();

  Future<int> fetchData() async {
    await Future.delayed(Duration(
      milliseconds: 2000,
    ));
    _data = _nextDatum++;
    return _data;
  }

  int getData() {
    return _data;
  }
}

class BaseClass extends StatefulWidget {
  final String title;
  final DataClass data;

  const BaseClass({Key key, this.title, this.data}) : super(key: key);

  @override
  _BaseClassState createState() => _BaseClassState();
}

class _BaseClassState extends State<BaseClass> {
  String title;
  DataClass data;

  @override
  Widget build(BuildContext context) {
    String dataStr = data == null ? ' - ' : '${data.getData()}';

    return Center(
      child: Text(
        '$title: $dataStr',
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    // initState gets called only ONCE
    title = widget.title;
    data = widget.data;
  }

  @override
  void didUpdateWidget(BaseClass oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.data != oldWidget.data) {
      data = widget.data;
    }
    if (widget.title != oldWidget.title) {
      title = widget.title;
    }
  }
}

class HomeWidget extends StatefulWidget {
  _HomeWidgetState createState() => _HomeWidgetState();
}

class _HomeWidgetState extends State<HomeWidget> {
  int pageIndex = 0;
  Map<String, DataClass> _dataMap = <String, DataClass>{};

  @override
  void initState() {
    super.initState();
    _init().then((result) {
      // Since we need to rebuild the widget with the resulting data,
      // make sure to use `setState`
      setState(() {
        _dataMap = result;
      });
    });
  }

  Future<Map<String, DataClass>> _init() async {
    // this fetches the data only once
    return <String, DataClass>{
      'home': DataClass()..fetchData(),
      'business': DataClass()..fetchData(),
      'school': DataClass()..fetchData(),
    };
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Bottom Navigation Bar'),
        ),
        body: choosePager(),
        bottomNavigationBar: BottomNavigationBar(
          currentIndex: pageIndex,
          items: <BottomNavigationBarItem>[
            BottomNavigationBarItem(
              icon: Icon(Icons.home),
              title: Text('Home'),
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.business),
              title: Text('Business'),
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.school),
              title: Text('School'),
            ),
          ],
          onTap: onTap,
        ),
      ),
    );
  }

  void onTap(int index) {
    setState(() {
      pageIndex = index;
    });
  }

  Widget choosePager() {
    Widget result;

    switch (pageIndex) {
     // it doesn't matter if you create a new BaseClass() a hundred times, flutter is optimized enough to not care. The `initState()` gets called only once.  You're fetching the data only once.
      case 0:
        result = BaseClass(
          title: 'Home',
          data: _dataMap['home'],
        );
        break;
      case 1:
        result = BaseClass(
          title: 'Business',
          data: _dataMap['business'],
        );
        break;
      case 2:
        result = BaseClass(
          title: 'School',
          data: _dataMap['school'],
        );
        break;
      default:
        result = Text('Unknown');
        break;
    }
    return result;
  }
}

0
投票

请尝试此代码,我已经编辑了BaseWidget类

  class BaseWidget extends StatefulWidget {
  final String title;

  BaseWidget(this.title);

  _BaseWidgetState createState() => _BaseWidgetState();
}

class _BaseWidgetState extends State<BaseWidget> {


  @override
  Widget build(BuildContext context) {
    return Center(child: Text(widget.title));
  }
}

0
投票

经过很多RND我用IndexedStack解决了我的问题。它根据索引显示子项列表中的单个Child。

Widget build(BuildContext context)_HomeWidgetState方法被调用时,它将初始化所有的孩子。因此,每次切换选项卡时,都不会重新初始化对象。

这是我的完整代码

class BaseWidget extends StatefulWidget {
  final String title;

  BaseWidget(this.title);

  _BaseWidgetState createState() => _BaseWidgetState();
}

class _BaseWidgetState extends State<BaseWidget> {
  @override
  Widget build(BuildContext context) {
    return Center(child: Text(widget.title));
  }
}

class HomeWidget extends StatefulWidget {
  _HomeWidgetState createState() => _HomeWidgetState();
}

class _HomeWidgetState extends State<HomeWidget> {
  int _pageIndex = 0;
  List<Widget> _children;

  @override
  void initState() {
    super.initState();

    _children = [
      BaseWidget('Home'),
      BaseWidget('Business'),
      BaseWidget('School')
    ];
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Bottom Navigation Bar'),
        ),
        body: IndexedStack(
          children: _children,
          index: _pageIndex,
        ),
        bottomNavigationBar: BottomNavigationBar(
          currentIndex: _pageIndex,
          items: <BottomNavigationBarItem>[
            BottomNavigationBarItem(
                icon: Icon(Icons.home), title: Text('Home')),
            BottomNavigationBarItem(
                icon: Icon(Icons.business), title: Text('Business')),
            BottomNavigationBarItem(
                icon: Icon(Icons.school), title: Text('School')),
          ],
          onTap: onTap,
        ),
      ),
    );
  }

  void onTap(int index) {
    setState(() {
      _pageIndex = index;
    });
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.