在 Flutter 中使用 Hook Widget 强制构建小部件树

Force a widget tree build using a Hook Widget in Flutter

我有一个动态接受未来列表的页面和一个回调来获取未来列表以接收数据并能够在刷新时刷新它。简化版本如下所示:

class ListSearchPage<T> extends StatefulWidget {

  final Future<List<T>> itemsFuture;
  final ValueGetter<Future<List<T>>> getItemsFuture;

  const ListSearchPage({Key key, this.getItemsFuture, this.itemsFuture})
      : super(key: key);

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


class _ListSearchPageState<T> extends State<ListSearchPage> {
  Future<List<T>> itemsFuture;
  TextEditingController _controller;

  @override
  void initState() {
    itemsFuture = widget.itemsFuture;
    _controller = TextEditingController();
    super.initState();
  }

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

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
              future:
                  itemsFuture != null ? itemsFuture : widget.getItemsFuture(),
              builder: (context, snapshot) {

                   return RefreshIndicator(
                     onRefresh: () async {
                        setState(() {
                          itemsFuture = null;
                          _controller.text = '';
                        });
                      },
                     child: ...
                   );
                });
    }
}

所以第一次,页面加载时已经加载了未来。当用户刷新时,我将未来标记为 null,以便调用回调并重新获取数据。

我现在正在尝试在整个应用程序中实施 flutter_hooks,并且我已将此小部件重构为如下所示(简化版):


class ListSearchPage<T> extends HookWidget {
  
  final Future<List<T>> itemsFuture;
  final ValueGetter<Future<List<T>>> getItemsFuture;

  const ListSearchPage({Key key, this.getItemsFuture, this.itemsFuture})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    final itemsFutureNotifier = useState(this.itemsFuture);
    final TextEditingController _controller = useTextEditingController();
    return FutureBuilder(
              future:
              itemsFutureNotifier.value != null ? itemsFutureNotifier.value : getItemsFuture(),
              builder: (context, snapshot) {
                   return RefreshIndicator(
                     onRefresh: () async {
                        itemsFutureNotifier.value = null;
                        _controller.text = '';
                      },
                     child: ...
                   );
                });
    }

}

这是第一次工作,但是之后该值一直被分配给 null,因此值通知程序不会收到有关更改的通知。在这种情况下,如何像以前一样强制重建小部件?作为奖励,您是否看到更好的解决方案?

提前致谢。

更新

这是itemsFuture

final future = useMemoized(() => repository.fetchData());

这是getItemsFuture

() => repository.fetchData()

其背后的想法是在打开搜索页面之前获取数据。在我的用例中有效。

我找到了解决问题的方法,但我不会 post 将它作为答案,因为我不相信它是干净的,我宁愿看看是否有人找到了正确的方法.

当前解

@override
  Widget build(BuildContext context) {
    // feels like a dirty solution for rebuilding on refresh
    final counterNotifier = useState(0);
    final itemsFutureNotifier = useState(this.itemsFuture);
    final TextEditingController _controller = useTextEditingController();

 return ValueListenableBuilder(
            valueListenable: counterNotifier,
            builder: (context, value, child) {
return FutureBuilder(
              future:
              itemsFutureNotifier.value != null ? itemsFutureNotifier.value : getItemsFuture(),
              builder: (context, snapshot) {
                   return RefreshIndicator(
                     onRefresh: () async {
                        counterNotifier.value++;
                        itemsFutureNotifier.value = null;
                        _controller.text = '';
                      },
                     child: ...
                   );
                });
});

如您所见,我现在有一个计数器通知程序,它实际上会重建 ValueListenableBuilder 并使 FutureBuilder 获取数据

我觉得itemsFuture没必要设置为null(因为可以是useState里面的初始语句)。

@override
Widget build(BuildContext context) {
  final fetchData = useState(itemsFuture ?? getItemsFuture());

  return Scaffold(
    body: FutureBuilder(
      future: fetchData.value,
      builder: (context, snapshot) {
        return RefreshIndicator(
          onRefresh: () async {
            fetchData.value = getItemsFuture();
          },
          child: ...
        );
      },
    ),
  );
}