У меня есть несколько страниц в моем приложении, завернутые в Offstage виджеты. Каждая страница использует пакет провайдера для отображения на основе обновлений состояния (например, пользователь что-то делает, мы делаем сетевой вызов и отображаем результат).
Поскольку страницы завернуты в виджеты Offstage, методы build() (и последующие сетевые вызовы) вызываются, даже если это не текущая страница.
Есть ли способ внутри метода build() узнать, находится ли виджет в данный момент вне сцены (и если да, то пропустить любую дорогостоящую логику)?
Я предполагаю, что могу работать с глобальным состоянием и т. д., но мне было интересно, есть ли что-нибудь встроенное в отношении самого виджета Offstage, похожее на mounted
Это нужно для того, чтобы сохранить состояние страницы. Я тоже смотрел на Visibility, но, видимо, он использует Offstage за кулисами, если вы хотите сохранить состояние.





Возможно, это неприменимо к вам, но вы можете решить эту проблему, просто не используя Offstage. Рассмотрим это приложение:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool showFirst = true;
void switchPage() {
setState(() {
showFirst = !showFirst;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Stack(children: [
Offstage(offstage: !showFirst,child: A("first", switchPage)),
Offstage(offstage: showFirst,child: A("second", switchPage)),
]))));
}
}
class A extends StatelessWidget {
final String t;
final Function onTap;
const A(this.t, this.onTap, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
print('$t is building');
return TextButton(onPressed: ()=> onTap(), child: Text(t));
}
}
По отпечаткам вы заметите, что обе страницы построены. Но если переписать вот так без Offstage, то строится только видимый:
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Stack(children: [
if (showFirst) A("first", switchPage),
if (!showFirst) A("second", switchPage),
]))));
}
Спасибо за предложение, но - поправьте меня, если я ошибаюсь - это не поддерживает состояние страницы? Таким образом, если на каждой странице есть дополнительная навигация, вы потеряете свою позицию и т. д.
@divillysausages ах да, это, вероятно, не поддерживает государство, нет
Если вы хотите просто поддерживать состояние своих страниц, вы можете использовать https://api.flutter.dev/flutter/widgets/AutomaticKeepAliveClientMixin-mixin.html , вы можете проверить этот блог, например, использование, https: //medium.com/manabie/flutter-simple-cheatsheet-4370a68f98b3
Это интересно, хотя строка their build methods must call super.build (though the return value should be ignored) подразумевала бы, что build() все равно вызывается, просто она не используется?
Я ценю ответ, кстати, хотя мой вопрос скорее "как я могу узнать, если я не на сцене", а не "как я могу сохранить состояние живым"
Я сделал индивидуальный механизм для цели, которую вы хотите достичь:
Во-первых, я объявляю новый Map<String, bool> в отдельном файле, который будет содержать значение offStage bool с ключом каждого виджета класса.
Map<String, bool> offStageMap = {};
затем в реализации StatefulWidget, где находится виджет вне сцены:
class ExampleWidget extends StatefulWidget {
ExampleWidget({super.key}) {
widgetMapKey = runtimeType.toString();
}
late final String widgetMapKey;
@override
State<ExampleWidget> createState() => _ExampleWidgetState();
}
class _ExampleWidgetState extends State<ExampleWidget> {
final bool defaultIsOffStaged = false;
bool? localStateIsOffStages;
@override
void initState() {
offStageMap[widget.widgetMapKey] ??= defaultIsOffStaged;
super.initState();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
bool previousIsOffStaged = offStageMap[widget.widgetMapKey]!;
setState(() {
localStateIsOffStages =
offStageMap[widget.widgetMapKey] = !previousIsOffStaged;
});
},
child: Offstage(
offstage: localStateIsOffStages ?? offStageMap[widget.widgetMapKey]!,
child: Container(),
),
);
}
} },
child: Offstage(
offstage: localStateIsOffStages ?? offStageMap[widget.widgetMapKey]!,
child: Container(),
),
);
}
}
позвольте мне объяснить, о чем идет речь.
сначала я объявил defaultIsOffStaged, где оно должно быть начальным значением offStage, когда на этой карте ничего не сохраняется.
когда этот виджет вставляется в дерево виджетов (вызывается initState()), widget.widgetMapKey виджета ExampleWidget будет сохранен на этой карте со значением по умолчанию, которое равно defaultIsOffStaged.
offStageMap[widget.widgetMapKey] ??= defaultIsOffStaged;
в свойстве вне сцены виджета OffStage, в этой строке:
offstage: localStateIsOffStages ?? offStageMap[widget.widgetMapKey]!,
значение nullable localStateIsOffStages будет нулевым в первый раз, поскольку оно еще не имеет значения, поэтому offStageMap[widget.widgetMapKey]!, равное defaultIsOffStaged, будет логическим значением вне сцены.
до сих пор то, что у нас есть, это карта, содержащая ключ, который принадлежит только ExampleWidget, который является его widget.widgetMapKey со значением offStage, верно?
теперь из всех мест вашего приложения вы можете получить значение offStage этого виджета с его widgetMapKey следующим образом:
print(offStageMap[ExampleWidget().widgetMapKey]); // true
теперь предположим, что вы хотите изменить свойство вне сцены этого виджета, в своем коде я использовал простой пример GestureDetector, поэтому, когда мы нажимаем в области Text("toggle offstage"), он переключается вне сцены, вот что происходит:
мы получили существующее значение в карте: bool предыдущаяIsOffStaged = offStageMap[widget.widgetMapKey]!;
затем назначьте противоположное этому ключу виджета на карте и логической переменной localStateIsOffStages, которая была обнуляемой, теперь она имеет значение.
и, как обычно, состояние обновляется, я завернул его в SetState(() {})
теперь виджет будет переключаться за пределы сцены, и каждый раз, когда ключ виджета на карте будет обновляться с этим новым значением.
localStateIsOffStages я объявил просто удерживать локальное состояние, когда это происходит, пока состояние StatefulWidget обновляется.
после того, как StatefulWidget будет удален (когда вы выберете маршрут в качестве примера) и снова откроете этот маршрут, initState() будет выполнен, но поскольку теперь у нас есть запись на карте, она не равна нулю, поэтому внутри initState() ничего не произойдет.
localStateIsOffStages будет нулевым, поэтому свойство offStage виджета Offstage будет значением из карты, которое является предыдущим значением перед удалением виджета.
вот и все, из других мест вы можете проверить значение вне сцены этого конкретного виджета следующим образом:
print(offStageMap[ExampleWidget().widgetMapKey])
вы можете сделать это для всех ваших страниц виджетов, так что у вас будет карта, содержащая все значения offStage.
Я сделал еще один шаг вперед и сделал те методы, которые, я думаю, помогут:
это вернет List со страницами, где значение истинно.
List<String> offstagedPages() {
List<String> isOffStagedPages = [];
offStageMap.forEach((runtimeType, isOffStaged) {
if (isOffStaged) {
isOffStagedPages.add(runtimeType);
}
});
return isOffStagedPages;
}
это вернет true, если страница не подготовлена, и false, если нет:
bool isPageWidgetOffStaged(String runtimeType) {
if (offStageMap.containsKey(runtimeType)) {
return offStageMap[runtimeType]!;
}
return false;
}
Надеюсь, это немного поможет.
Если вы используете Navigator, вы можете просто расширить NavigatorObserver. Затем вы получите didpush и didpop, используйте состояние для управления жизненным циклом элемента, вы получите удовольствие от страницы onPause и onResume.
Вы можете попробовать найти родительский виджет OffStage и посмотреть, является ли свойство вне сцены истинным или ложным.
@override
Widget build(BuildContext context) {
final offstageParent = context.findAncestorWidgetOfExactType<Offstage>();
if (offstageParent != null && offstageParent.offstage == false) {
// widget is currently offstage.
print('offstaged child');
} else {
// widget is not offstage
print('non-offstaged child');
}
return const Text('Example Widget');
}
Нужно ли вообще использовать
Offstage? может быть есть лучшее решение без него