使用 flutter_hooks 进行不必要的重建
Unnecessary rebuilds with flutter_hooks
我正在尝试同时使用 StateNotifier
、Provider
、flutter_hooks
和 functional_widget
。
我在小部件层次结构中看到了我不期望的重建。我在下面设置了一个简单的用例。
我的集团有一个方法 updateName()
定义为
void updateName() {
var name = this.state.userName + "X";
updateState(this.state.copyWith(userName: name));
}
作为测试,我在按下其中一个 MenuItems 或用户点击 Update Name
按钮时调用此方法。我的期望是,当按下 updateName()
并更新 StateNotifiers state
时,只会调用 userAccountHeader
构建函数,因为它是唯一通过以下方式收听名称的函数:
var name = useStateValue(bloc, (MenuState s) => s.userName);
但是,从我的日志中我可以看到正在重建这棵树中的所有小部件。
flutter: menuList.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: userAccountHeader.build
import 'package:apptree_client/components/menu/menu_bloc.dart';
import 'package:apptree_client/hooks/hooks.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:functional_widget_annotation/functional_widget_annotation.dart';
import 'menu_item_view.dart';
part 'menu.g.dart';
@hwidget
Widget menuDrawer(BuildContext context) {
var bloc = useMenuBloc();
return Drawer(child: menuList(context, bloc));
}
@hwidget
Widget userAccountHeader(BuildContext context, MenuBloc bloc) {
var name = useStateValue(bloc, (MenuState s) => s.userName);
print("userAccountHeader.build");
return DrawerHeader(
child: Column(
children: [
Text(name),
MaterialButton(
onPressed: () => bloc.updateName(),
child: Text("Update name"),
)
],
),
decoration: BoxDecoration(
color: Colors.blue,
),
);
}
@hwidget
Widget menuList(BuildContext context, MenuBloc bloc) {
var menuItems = useStateValue(bloc, (MenuState s) => s.menuItems);
print("menuList.build");
var menuItemsWidgets =
menuItems.map((item) => menuListItem(context, item, bloc)).toList();
return ListView(children: [
userAccountHeader(context, bloc),
...menuItemsWidgets,
]);
}
@hwidget
Widget menuListItem(BuildContext context, MenuItemView view, MenuBloc bloc) {
print("menuListItem.build");
return ListTile(
title: Text(view.title),
onTap: () => bloc.updateName(),
);
}
MenuBloc useMenuBloc() {
final context = useContext();
return useMemoized(
() => MenuBloc(
config: Provider.of(context, listen: false),
appSettings: Provider.of(context, listen: false),
),
);
}
R useStateValue<T, R>(StateNotifier<T> notifier, R selector(T value)) {
// ignore: invalid_use_of_protected_member
final state = useState<R>(selector(notifier.state));
useEffect(() {
return notifier.addListener((s) {
var newVal = selector(s);
if (!const DeepCollectionEquality().equals(newVal, state.value)) {
state.value = newVal;
}
});
}, [notifier]);
return state.value;
}
您正在直接使用 functional_widget 函数,而不是使用生成的小部件。
因此,不是每个小部件都有自己的上下文并且能够单独重建,而是创建了一个大的小部件,就像您只对小部件的一部分而不是小部件使用函数一样 类.
为了解决这个问题,让所有的小部件函数调用都以大写开头,而不是使用生成的小部件
我正在尝试同时使用 StateNotifier
、Provider
、flutter_hooks
和 functional_widget
。
我在小部件层次结构中看到了我不期望的重建。我在下面设置了一个简单的用例。
我的集团有一个方法 updateName()
定义为
void updateName() {
var name = this.state.userName + "X";
updateState(this.state.copyWith(userName: name));
}
作为测试,我在按下其中一个 MenuItems 或用户点击 Update Name
按钮时调用此方法。我的期望是,当按下 updateName()
并更新 StateNotifiers state
时,只会调用 userAccountHeader
构建函数,因为它是唯一通过以下方式收听名称的函数:
var name = useStateValue(bloc, (MenuState s) => s.userName);
但是,从我的日志中我可以看到正在重建这棵树中的所有小部件。
flutter: menuList.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: menuListItem.build
flutter: userAccountHeader.build
import 'package:apptree_client/components/menu/menu_bloc.dart';
import 'package:apptree_client/hooks/hooks.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:functional_widget_annotation/functional_widget_annotation.dart';
import 'menu_item_view.dart';
part 'menu.g.dart';
@hwidget
Widget menuDrawer(BuildContext context) {
var bloc = useMenuBloc();
return Drawer(child: menuList(context, bloc));
}
@hwidget
Widget userAccountHeader(BuildContext context, MenuBloc bloc) {
var name = useStateValue(bloc, (MenuState s) => s.userName);
print("userAccountHeader.build");
return DrawerHeader(
child: Column(
children: [
Text(name),
MaterialButton(
onPressed: () => bloc.updateName(),
child: Text("Update name"),
)
],
),
decoration: BoxDecoration(
color: Colors.blue,
),
);
}
@hwidget
Widget menuList(BuildContext context, MenuBloc bloc) {
var menuItems = useStateValue(bloc, (MenuState s) => s.menuItems);
print("menuList.build");
var menuItemsWidgets =
menuItems.map((item) => menuListItem(context, item, bloc)).toList();
return ListView(children: [
userAccountHeader(context, bloc),
...menuItemsWidgets,
]);
}
@hwidget
Widget menuListItem(BuildContext context, MenuItemView view, MenuBloc bloc) {
print("menuListItem.build");
return ListTile(
title: Text(view.title),
onTap: () => bloc.updateName(),
);
}
MenuBloc useMenuBloc() {
final context = useContext();
return useMemoized(
() => MenuBloc(
config: Provider.of(context, listen: false),
appSettings: Provider.of(context, listen: false),
),
);
}
R useStateValue<T, R>(StateNotifier<T> notifier, R selector(T value)) {
// ignore: invalid_use_of_protected_member
final state = useState<R>(selector(notifier.state));
useEffect(() {
return notifier.addListener((s) {
var newVal = selector(s);
if (!const DeepCollectionEquality().equals(newVal, state.value)) {
state.value = newVal;
}
});
}, [notifier]);
return state.value;
}
您正在直接使用 functional_widget 函数,而不是使用生成的小部件。 因此,不是每个小部件都有自己的上下文并且能够单独重建,而是创建了一个大的小部件,就像您只对小部件的一部分而不是小部件使用函数一样 类.
为了解决这个问题,让所有的小部件函数调用都以大写开头,而不是使用生成的小部件