Преобразование StatefulWidgets в StatelessWidget во Flutter: методы и примеры

Во Flutter StatefulWidgets и StatelessWidget — это два основных типа виджетов, используемых для создания пользовательских интерфейсов. В то время как StatefulWidgets используются, когда вам нужно управлять изменяемым состоянием внутри виджета, StatelessWidget используется, когда состояние виджета остается постоянным. Однако могут быть случаи, когда вы захотите преобразовать виджет с сохранением состояния обратно в виджет без сохранения состояния. В этой статье мы рассмотрим несколько методов достижения такого преобразования вместе с примерами кода.

Метод 1: извлечение метода сборки виджета с отслеживанием состояния
Первый метод включает в себя извлечение метода сборки из виджета с отслеживанием состояния и размещение его в отдельном виджете без состояния. Вот пример:

class MyStatefulWidget extends StatefulWidget {
  @override
  _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  int _counter = 0;
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Counter: $_counter'),
        RaisedButton(
          child: Text('Increment'),
          onPressed: _incrementCounter,
        ),
      ],
    );
  }
}
class MyStatelessWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Counter: 0'),
        RaisedButton(
          child: Text('Increment'),
          onPressed: () {
            // Implement the desired behavior
            // ...
          },
        ),
      ],
    );
  }
}

В этом примере метод сборки из _MyStatefulWidgetStateизвлекается в MyStatelessWidget, а код, связанный с состоянием, удаляется.

Метод 2: использование библиотек управления состоянием.
Другой подход заключается в использовании библиотек управления состоянием, таких как Provider, Riverpod или MobX, для управления состоянием отдельно от самого виджета. Отделив состояние от виджета, вы можете преобразовать виджет Stateful в StatelessWidget, устраняя необходимость во внутреннем управлении состоянием. Вот пример использования пакета Provider:

class MyStatefulWidget extends StatefulWidget {
  @override
  _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  int _counter = 0;
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Counter: $_counter'),
        RaisedButton(
          child: Text('Increment'),
          onPressed: _incrementCounter,
        ),
      ],
    );
  }
}
class MyStatelessWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = Provider.of<Counter>(context);
    return Column(
      children: [
        Text('Counter: ${counter.value}'),
        RaisedButton(
          child: Text('Increment'),
          onPressed: () {
            counter.increment();
          },
        ),
      ],
    );
  }
}
class Counter extends ChangeNotifier {
  int _value = 0;
  int get value => _value;
  void increment() {
    _value++;
    notifyListeners();
  }
}

В этом примере состоянием управляет класс Counter, который расширяет ChangeNotifierиз пакета поставщика. MyStatelessWidgetтеперь использует экземпляр Counter, полученный от поставщика, для отображения и обновления значения счетчика.

Метод 3: использование InheritedWidgets или InheritedModel
В некоторых случаях вы можете использовать InheritedWidgets или InheritedModel для распространения состояния вниз по дереву виджетов без необходимости использования StatefulWidget. Используя эти унаследованные виджеты, вы можете исключить необходимость в StatefulWidget и связанном с ним состоянии. Вот упрощенный пример:

class MyStatefulWidget extends StatefulWidget {
  @override
  _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  int _counter = 0;
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
  @override
  Widget build(BuildContext context) {
    return MyInheritedWidget(
      counter: _counter,
      incrementCounter: _incrementCounter,
      child: Column(
        children: [
          MyChildWidget(),
        ],
      ),
    );
  }
}
class MyInheritedWidget extends InheritedWidget {
  final int counter;
  final VoidCallback incrementCounter;
  MyInheritedWidget({
    Key key,
    @required this.counter,
    @required this.incrementCounter,
    @required Widget child,
  }) : super(key: key, child: child);
  static MyInheritedWidget of(BuildContext context) {
    return context.dependOnInheritedWidget<MyInheritedWidget>();
  }
  @override
  bool updateShouldNotify(covariant MyInheritedWidget oldWidget) {
    return counter != oldWidget.counter;
  }
}
class MyChildWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final inheritedWidget = MyInheritedWidget.of(context);
    return Column(
      children: [
        Text('Counter: ${inheritedWidget.counter}'),
        RaisedButton(
          child: Text('Increment'),
          onPressed: inheritedWidget.incrementCounter,
        ),
      ],
    );
  }
}

В этом примере MyInheritedWidgetиспользуется для распространения значения счетчика и функции приращения вниз по дереву виджетов. MyChildWidgetполучает доступ к значению счетчика и функции приращения непосредственно из унаследованного виджета.

В этой статье мы рассмотрели несколько методов преобразования виджета с сохранением состояния обратно в виджет без состояния во Flutter. Эти методы включают извлечение метода сборки, использование библиотек управления состоянием и использование InheritedWidgets или InheritedModel для распространения состояния. Понимая эти методы, вы сможете эффективно управлять состоянием виджета и выбирать подходящий тип виджета для вашего приложения Flutter.