child 中的 Flutter 触发动作与 InheritedWidget

Flutter trigger action in child with InheritedWidget

我有一个dice-rollingMaterialApp。当按下 floatingButton 时,我想在 children 上触发 "dice-roll"。我正在尝试使用 InheritedWidget,但我见过的大多数示例似乎都在做相反的事情,从 child.

触发 parent 更改

无国籍人Parent:

class DiceApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Container(
          child: Center(
            child: Row(
              children: <Widget>[
                RollTrigger(
                  roller: Roller(),
                  child: Die(),
                ),
                RollTrigger(
                  roller: Roller(),
                  child: Die(),
                ),
              ],
            ),
          ),
        ),
        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.casino),
          onPressed: () {
            // Trigger the dice roll on each
          },
        ),
      ),
    );
  }
}
}

InheritedWidget:

class RollTrigger extends InheritedWidget {
  RollTrigger({this.roller, this.child});

  final roller;
  final Widget child;

  static RollTrigger of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<RollTrigger>();
  }

  @override
  bool updateShouldNotify(RollTrigger oldWidget) => true;
}

我正在尝试使用Rollerclass来触发滚动动作:

class Roller {
  bool rolling = false;

  void roll(_DieState die) {
    die.roll();
  }
}

最后是 Stateful Die:

class Die extends StatefulWidget {

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

class _DieState extends State<Die> {

  int value = 0;
  final _random = new Random();
  roll() {
    this.value = 1 + _random.nextInt(6 - 1);
  }

  _DieState();

  @override
  Widget build(BuildContext context) {
    var roller = RollTrigger.of(context).roller;

    return Text(this.value.toString());
  }
}

这似乎应该更简单,但我在这里陷入困境。

编辑:我根据建议将 Roller 置于顶层进行更新。我仍然不确定如何触发底部小部件的重建:

final Roller roller = Roller();
...
  RollTrigger(
    roller: this.roller,
    child: Die(),
)

然后我将 roll 方法放在 Roller 中:

class Roller {

  int value = 0;

  final _random = new Random();

  void roll() {

    this.value = 1 + _random.nextInt(6 - 1);

    print(this.value);

  }
}

Roller赋值:

class _DieState extends State<Die> {
  @override
  Widget build(BuildContext context) {
    final roller = RollTrigger.of(context).roller;

    return Text(roller.value.toString());
  }
}

最后,在顶层调用 roll 方法:

        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.casino),
          onPressed: () {
            this.roller.roll();
          },
        ),

这会更新 Roller 的值,但不会更改 _DieState 的值...这就是我要解决的问题。那里有很多有趣的模式,但我 认为 我正在努力解决的是基本实现。

This seems like it should be simpler, but I'm tying myself in knots here.

是的。应该会简单很多!

您需要利用观察者设计模式。在 Flutter 中,可观察对象是 ChangeNotifier.

包提供者将提供很大帮助。您创建的所有小部件 类 都可以 StatelessWidget.

import 'dart:math';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class Die with ChangeNotifier {
  //
  // _value can be one of null, 1, 2, 3, 4, 5, 6
  // A value of null signifies the die is currently rolling.
  //
  int _value = 1;

  final Random _random = Random();

  int get value {
    return this._value;
  }

  Future<void> roll() async {
    if (this._value == null) {
      // already rolling
      return;
    }

    this._value = null;
    this.notifyListeners();

    await Future.delayed(Duration(seconds: 1));

    this._value = 1 + this._random.nextInt(6);
    this.notifyListeners();
  }
}

// ---------------------------------------------------------

void main() {
  return runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(final BuildContext context) {
    return MaterialApp(
      title: 'Die Rolling App',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(final BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<Die>(
          create: (final BuildContext context) {
            return Die();
          },
        ),
      ],
      child: Scaffold(
        appBar: AppBar(title: const Text("Die Rolling Game")),
        body: Center(child: DieDisplay()),
        floatingActionButton: RollFAB(),
      ),
    );
  }
}

class DieDisplay extends StatelessWidget {
  @override
  Widget build(final BuildContext context) {
    return Consumer<Die>(
      builder: (final BuildContext context, final Die die, final Widget child) {
        return Text((die.value == null) ? 'rolling' : die.value.toString());
      },
    );
  }
}

class RollFAB extends StatelessWidget {
  @override
  Widget build(final BuildContext context) {
    return FloatingActionButton(
      onPressed: () {
        final Die die = Provider.of<Die>(context, listen: false);
        die.roll();
      },
      tooltip: 'Roll',
      child: const Icon(Icons.casino),
    );
  }
}