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

时间:2019-04-07 18:03:21

标签: dart flutter

我想使用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()方法中使用了这些属性。

3 个答案:

答案 0 :(得分: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;
  }
}

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

一些示例代码:

/// 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;
  }
}

答案 1 :(得分: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));
  }
}

答案 2 :(得分:0)

经过大量RND之后,我使用IndexedStack解决了我的问题。它根据索引从子级列表中显示单个“子级”。

当调用Widget build(BuildContext context)的{​​{1}}方法时,它将初始化所有子项。因此,无论何时切换选项卡,该对象都不会重新初始化。

这是我的完整代码

_HomeWidgetState