Действие триггера у ребенка с InheritedWidget - PullRequest
1 голос
/ 07 февраля 2020

У меня есть игра в кости MaterialApp. Когда нажата floatingButton, я хочу вызвать «бросок костей» на детей. Я пытаюсь использовать InheritedWidget, но большинство примеров, которые я видел, по-видимому, делают противоположное, инициируют смену родителя у потомка.

Родитель без состояния:

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;
}

Я пытаюсь использовать класс Roller для запуска действия броска:

class Roller {
  bool rolling = false;

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

И, наконец, Stateful D ie:

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());
  }
}

Кажется, это должно быть проще, но я тут завязываю себя.

Редактировать: я обновляю, ставя Роллер на верхний уровень за предложение. Я до сих пор не знаю, как вызвать перестроение для нижнего виджета:

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 ... это проблема, которую я пытаюсь решить. Есть много интересных шаблонов, но я думаю , с чем я борюсь, это реализация basi c.

1 Ответ

1 голос
/ 08 февраля 2020

Кажется, все должно быть проще, но я тут завязываюсь.

Да. Это должно быть намного проще!

Вам нужно воспользоваться преимуществами шаблона проектирования наблюдателя. Во 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),
    );
  }
}
...