gpt4 book ai didi

flutter - 如何将状态向上传递到小部件树?

转载 作者:行者123 更新时间:2023-12-05 05:31:15 25 4
gpt4 key购买 nike

这是我的代码结构:

class SomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
Widget1(), // How to pass `FooPageState` to this widget?
Widget2(),
);
}
}


class Widget2 extends StatefulWidget {
@override
State<Widget2> createState() => _Widget2State();
}

class _Widget2State extends State<Widget2> {
final GlobalKey<FooPageState> _globalKey = GlobalKey();

@override
Widget build(BuildContext context) {
return Column(
InnerWidget1(),
FooPage(
key: _globalKey,
),
InnerWidget2(
pageState: _globalKey.currentState, // Passing `FooPageState`
),
);
}
}

我需要将 FooPageState 传递给 Widget1,我该怎么做? FooPage 只是一个普通的 StatefulWidget,其状态为 FooPageState

一些解决方法:

  1. SomePage 中声明 _globalKey 并将其传递给 Widget1Widget2

  2. SomePage 转换为 StatefulWidget,在其中定义一个回调,将其传递给 Widget2 然后在类似 的方法中>SchedulerBinding.instance.addPostFrameCallback,调用传递过来的回调,回调又可以通过setState调用定义的_SomePageState回调,传递FooPageStateWidget1。使用 NotificationNotificationListener 也可以完成同样的事情。

最佳答案

行为:

一个带有按钮的小部件,可以读取和设置另一个小部件状态的值。计数器在状态读取器(而不是状态所有者)上递增,并且更改反射(reflect)在两个小部件中。

enter image description here

  • 这可以通过使用 InheritedWidget 来实现因为它的数据可以被 InheritedWidget 下面的小部件访问通过 BuildContext 在小部件树中.

  • 接下来的每个步骤都包含对所解决问题的简短描述,然后是注释代码以及对各个部分的更完整解释。

首先,让我们定义我们的入口点;显而易见/容易理解。

void main() => runApp(MaterialApp(home: Scaffold(body: SharedStateWidget())));

其次,让我们创建一个辅助类,允许我们读/写 FooPageState .

/// Helper class. In this case, the template parameter T will be [FooPageState].
class Data<T> {
T? value;
}

第三,让我们创建 SharedStateWidget允许访问 FooPageState通过 InheritedWidget类(class)行为。

/// Parent widget of any widget that must access [FooPageState].
class SharedStateWidget extends InheritedWidget {
SharedStateWidget({Key? key}) : super(key: key, child: const SomePage());

/// The helper class is used to assign to [data.value], because this widget
/// itself is immutable (won't rebuild).
final Data<FooPageState> data = Data<FooPageState>();

@override
bool updateShouldNotify(SharedStateWidget oldWidget) {
return data != oldWidget.data || data.value != oldWidget.data.value;
}

/// Ensure that this method is ONLY called by children of [SharedStateWidget],
/// because the null-check operator `!` is used to avoid returning a nullable.
static SharedStateWidget of(BuildContext context) =>
context.dependOnInheritedWidgetOfExactType<SharedStateWidget>()!;
}

第四个是你的 SomePage包含一些其他小部件的小部件(在本例中称为 FooStateReceiver ),想要读取 FooPageState .

/// [SomePage] is a child of [SharedStateWidget], which is an [InheritedWidget].
/// Therefore, itself and its children can access [SharedStateWidget] through
/// the [SharedStateWidget.of] static method.
class SomePage extends StatelessWidget {
const SomePage({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: const [
/// [FooPageState] will be accessed through [SharedStateWidget.of].
FooStateReceiver(),
FooPage(),
],
);
}
}

第五个是需要的类FooPageState在所述状态上调用一些外部操作。

/// A widget that uses [FooPageState] to perform some external operation.
class FooStateReceiver extends StatefulWidget {
const FooStateReceiver({Key? key}) : super(key: key);

@override
State<FooStateReceiver> createState() => _FooStateReceiverState();
}

class _FooStateReceiverState extends State<FooStateReceiver> {
FooPageState? _fooState;

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

/// The [SomePageInheritedWidget.of] cannot be called before [initState].
/// Call [setState] so that this widget itself is rebuilt after reading
/// [SharedStateWidget.data.value].
SchedulerBinding.instance.addPostFrameCallback(
(timeStamp) => setState(
() => _fooState = SharedStateWidget.of(context).data.value,
),
);
}

@override
Widget build(BuildContext context) {
return SizedBox(
width: 64.0,
height: 64.0,
child: FloatingActionButton(
/// Updates this widget's state using the other widget's state.
onPressed: () => setState(() => ++_fooState?.counter),
child: Text(
"${_fooState?.counter}",
style: const TextStyle(fontSize: 20.0),
),
),
);
}
}

第六个是它的状态必须被另一个小部件访问的类,这是你的FooPage .

/// This is the [StatefulWidget] whose state we want to access from
/// [FooStateReceiver].
class FooPage extends StatefulWidget {
const FooPage({Key? key}) : super(key: key);

@override
State<FooPage> createState() => FooPageState();
}

class FooPageState extends State<FooPage> {
int _counter = 0;
int get counter => _counter;

/// The method that you want to call from [FooStateReceiver].
/// Sets [_counter] if it is different from [value] and rebuilds.
set counter(int value) {
if (_counter == value) return;
setState(() => _counter = value);
}

@override
Widget build(BuildContext context) {
/// Set [data.value] of the [StateWidget] so that it can be used by
/// [FooStateReceiver] whenever this widget is rebuilt.
SharedStateWidget.of(context).data.value = this;

return SizedBox(
width: 64.0,
height: 64.0,
child: Align(
child: Text("$counter", style: const TextStyle(fontSize: 20.0)),
),
);
}
}

使用 riverpod 的行为完全相同:

void main() => runApp(
const MaterialApp(home: ProviderScope(child: Scaffold(body: SomePage()))));

class SomePage extends StatelessWidget {
const SomePage({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: const [
FooStateReceiver(),
FooPage(),
],
);
}
}

final fooStateProvider = StateProvider<FooPageState?>((ref) => null);

class FooStateReceiver extends StatefulWidget {
const FooStateReceiver({Key? key}) : super(key: key);

@override
State<FooStateReceiver> createState() => _FooStateReceiverState();
}

class _FooStateReceiverState extends State<FooStateReceiver> {
@override
Widget build(BuildContext context) {
return SizedBox(
width: 64.0,
height: 64.0,
child: Consumer(
builder: (context, ref, child) {
final fooState = ref.watch(fooStateProvider);

return FloatingActionButton(
/// Updates this widget's state using the other widget's state.
onPressed: () => setState(() => ++fooState?.counter),
child: Text(
"${fooState?.counter}",
style: const TextStyle(fontSize: 20.0),
),
);
},
),
);
}
}

class FooPage extends ConsumerStatefulWidget {
const FooPage({Key? key}) : super(key: key);

@override
ConsumerState<FooPage> createState() => FooPageState();
}

class FooPageState extends ConsumerState<FooPage> {
int _counter = 0;
int get counter => _counter;

/// The method that you want to call from [FooStateReceiver].
/// Sets [_counter] if it is different from [value] and rebuilds.
set counter(int value) {
if (_counter == value) return;
setState(() => _counter = value);
}

@override
Widget build(BuildContext context) {
/// Update the provider state.
SchedulerBinding.instance.addPostFrameCallback(
(timeStamp) {
ref.watch(fooStateProvider.notifier).state = this;
},
);

return SizedBox(
width: 64.0,
height: 64.0,
child: Align(
child: Text("$counter", style: const TextStyle(fontSize: 20.0)),
),
);
}
}

结论:

  • 状态管理库将帮助/简化实现所需行为所需的代码。
  • 关于使用 riverpod,您说过“您想要状态,而不是数据” .但是,状态与任何其他数据/类一样。
  • 将您的行为与您的用户界面分开。查找 Flutter 的 BLoC 模式并实现它。它将清理您的代码,因为 UI 代码不应在其中实现复杂的数据行为。
  • 动画可能是个异常(exception),但即使是动画也可以分成它们自己的数据类。

关于flutter - 如何将状态向上传递到小部件树?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74410974/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com