无需重新构建父级子控件的Flutter setState

时间:2018-08-07 10:52:29

标签: android dart flutter

我有一个包含listViewfloatingActionButton的父级,我想在用户开始滚动时隐藏floatingActionButton,但我设法在父级小部件中做到了这一点,但这需要每次重新构建列表。

我已将floatingActionButton移到一个单独的类,因此我可以更新状态并仅重建该小部件,因为我遇到的问题是将数据从父类中的ScrollController传递给孩子通过导航执行此操作很简单,但在不重建父级的情况下缝了一个但更尴尬的东西!

4 个答案:

答案 0 :(得分:4)

您可以使用 StatefulBuilder 并使用其 setState 函数在其下构建小部件。

示例:

import 'package:flutter/material.dart';

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {

  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // put widget here that you do not want to update using _setState of StatefulBuilder
        Container(
          child: Text("I am static"),
        ),
        StatefulBuilder(builder: (_context, _setState) {
          // put widges here that you want to update using _setState
          return Column(
            children: [
              Container(
                child: Text("I am updated for $count times"),
              ),
              RaisedButton(
                  child: Text('Update'),
                  onPressed: () {
                    // Following only updates widgets under StatefulBuilder as we are using _setState
                    // that belong to StatefulBuilder
                    _setState(() {
                      count++;
                    });
                  })
            ],
          );
        }),
      ],
    );
  }
}

答案 1 :(得分:2)

当父级中的值发生变化时重建仅子级小部件的一种好方法是使用 ValueNotifierValueListenableBuilder。将 ValueNotifier 的实例添加到父级的 state 类中,并将要重建的小部件包装在 ValueListenableBuilder 中。

当您想要更改值时,请使用通知程序而不调用 setState,然后子小部件将使用新值重建。

import 'package:flutter/material.dart';

class Parent extends StatefulWidget {
  @override
  _ParentState createState() => _ParentState();
}

class _ParentState extends State<Parent> {
  ValueNotifier<bool> _notifier = ValueNotifier(false);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ElevatedButton(onPressed: () => _notifier.value = !_notifier.value, child: Text('toggle')),
        ValueListenableBuilder(
            valueListenable: _notifier,
            builder: (BuildContext context, bool val, Widget child) {
              return Text(val.toString());
            }),
      ],
    );
  }
}

答案 2 :(得分:0)

为了获得最佳性能,您可以围绕Scaffold创建自己的包装,该包装将body作为参数。在body中调用setState时,不会重建HideFabOnScrollScaffoldState小部件。

这是一种常见模式,也可以在诸如AnimationBuilder之类的核心小部件中找到。

import 'package:flutter/material.dart';

main() => runApp(MaterialApp(home: MyHomePage()));

class MyHomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  ScrollController controller = ScrollController();

  @override
  Widget build(BuildContext context) {
    return HideFabOnScrollScaffold(
      body: ListView.builder(
        controller: controller,
        itemBuilder: (context, i) => ListTile(title: Text('item $i')),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        child: Icon(Icons.add),
      ),
      controller: controller,
    );
  }
}

class HideFabOnScrollScaffold extends StatefulWidget {
  const HideFabOnScrollScaffold({
    Key key,
    this.body,
    this.floatingActionButton,
    this.controller,
  }) : super(key: key);

  final Widget body;
  final Widget floatingActionButton;
  final ScrollController controller;

  @override
  State<StatefulWidget> createState() => HideFabOnScrollScaffoldState();
}

class HideFabOnScrollScaffoldState extends State<HideFabOnScrollScaffold> {
  bool _fabVisible = true;

  @override
  void initState() {
    super.initState();
    widget.controller.addListener(_updateFabVisible);
  }

  @override
  void dispose() {
    widget.controller.removeListener(_updateFabVisible);
    super.dispose();
  }

  void _updateFabVisible() {
    final newFabVisible = (widget.controller.offset == 0.0);
    if (_fabVisible != newFabVisible) {
      setState(() {
        _fabVisible = newFabVisible;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: widget.body,
      floatingActionButton: _fabVisible ? widget.floatingActionButton : null,
    );
  }
}

或者,您也可以为FloatingActionButton创建一个包装器,但这可能会破坏过渡。

答案 3 :(得分:0)

我认为使用流更简单,也很容易。

您只需要在事件到达时将其发布到流中,然后使用流构建器来响应这些更改。

在这里,我基于小部件层次结构中的小部件焦点显示/隐藏组件。

我在这里使用了rxdart包,但我认为您不需要。还是您可能还是要这么做,因为大多数人还是会使用BloC模式。

import 'dart:async';
import 'package:rxdart/rxdart.dart';

class _PageState extends State<Page> {
  final _focusNode = FocusNode();

  final _focusStreamSubject = PublishSubject<bool>();
  Stream<bool> get _focusStream => _focusStreamSubject.stream;

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

    _focusNode.addListener(() {
      _focusStreamSubject.add(_focusNode.hasFocus);
    });
  }

  @override
  void dispose() {
    _focusNode.dispose();
    super.dispose();
  }


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: <Widget>[
          _buildVeryLargeComponent(),
          StreamBuilder(
            stream: _focusStream,
            builder: ((context, AsyncSnapshot<bool> snapshot) {
              if (snapshot.hasData && snapshot.data) {
                return Text("keyboard has focus")
              }
              return Container();
            }),
          )
        ],
      ),
    );
  }
}