Ненужные пересборки с помощью 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
будет вызвана функция build, поскольку она единственная, кто прослушивает имя через:
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;
}
1 ответ
Вы используете функции function_widget напрямую, вместо использования сгенерированных виджетов. Таким образом, вместо того, чтобы каждый виджет имел свой собственный контекст и мог перестраиваться отдельно, вы создали один большой виджет, точно так же, как когда вы используете только функции для частей виджета вместо классов виджетов.
Чтобы решить эту проблему, пусть все вызовы функций виджета начинаются с заглавной буквы, чтобы вместо этого использовать сгенерированный виджет.