Flutter: объединение виджетов с отслеживанием состояния в архитектуру приложения flutter-redux

После этого вопроса я понимаю, почему < strong> StatefulWidgets действительно имеет смысл в контексте приложения флаттера на основе Redux. И это то, чего я тоже пытаюсь достичь -

Страница, которая получает информацию из состояния всего приложения (зарегистрированные данные пользователя, данные API и т. д.), а также может отправлять действия из подключенной модели ViewModel, но та, которая ТАКЖЕ содержит виджеты с отслеживанием состояния с меньшей областью действия, кратковременным состоянием. такие вещи, как анимация, проверка пользовательского ввода на лету перед отправкой и изменение пользовательского интерфейса на основе действий пользователя.

Так что меня интересует, как это сделать, и я надеюсь, что кто-то здесь может мне помочь. В настоящее время все мои страницы представляют собой виджеты без сохранения состояния, подключенные к состоянию приложения через хранилище следующим образом:

class LoginPage extends StatelessWidget {
  final String TAG = "LoginPage";
  bool isGreen = false;

  LoginPage({Key key}) : super(key: key);

  void changeColor() {
    isGreen = !!isGreen;
  }


  @override
  Widget build(BuildContext context) {
    /// [StoreConnector] is used to convert store data (using the fromStore)
    /// into a ViewModel suitable for the page.
    return StoreConnector<AppState, LoginPageViewModel>(
        builder: (context, viewModel) {
          return Scaffold(
              body: Column(
               children: <Widget>[
                Container(...),
                Text(
                  text: viewModel.some_value_from_the_store,
                  color: isGreen ? Colors.green : Colors.red,
                ),
                ElevatedButton(
                  onPressed: () => changeColor(),
                  child: Text('Press to change color'),
                )
            ],
          ));
        },
        converter: LoginPageViewModel.fromStore);
  }
}

Здесь я просто пытаюсь просто изменить цвет текста внутри виджета LoginPage на основе щелчков пользователя, оставаясь при этом подключенным к магазину, чтобы пользовательский интерфейс продолжал обновляться с новой информацией о состоянии приложения по мере ее поступления.

Есть ли там ссылка на что-то подобное? может ли кто-нибудь привести пример или просто основные рекомендации, как этого добиться? кажется простым, но я борюсь с этим.


person Maoration    schedule 24.01.2021    source источник


Ответы (1)


Вот несколько идей, которые могут помочь.

Ваш виджет должен быть с отслеживанием состояния. Вы пытаетесь отслеживать цвет, который со временем будет меняться. Виджеты без сохранения состояния допускают только окончательные свойства - то есть вы можете инициировать их значение только при создании.

Ваша переменная isGreen и метод changeColor () должны быть частью объекта состояния. Ваш метод build () также пойдет туда.

Далее - когда вы вызываете свой метод, вы должны вызвать:

void changeColor() {
    setState(() {isGreen = !isGreen;});
}

Я думаю, что в вашем коде вы не переворачиваете значение (= !! iGreen совпадает с = isGreen). Но что более важно - вы не говорите фреймворку, что ваш виджет должен быть перестроен. Вы можете проверить это на себе: щелкните один раз (после того, как вы исправили «!!»). Ничего не случится. Если принудительно обновить в эмуляторе - вы должны увидеть, что цвет действительно изменился. Это потому, что вы обновили его вручную. setState () сделает это за вас: он запустит предоставленный вами код, а затем вызовет Flutter, чтобы сообщить ему обновить ваш виджет. Смотрите - flutter не имеет волшебного триггера и не наблюдает за вашим кодом, чтобы решить, когда обновлять ваш виджет. setState () сообщает ему об этом.

Как общее правило: состояние вашего приложения, то есть данные, совместно используемые несколькими виджетами (или страницами), должны быть «приподняты» по дереву виджетов и сохранены в классах провайдеров.

Виджеты с отслеживанием состояния должны хранить только данные, непосредственно связанные с ними, и только с ними. Обычно это данные, которые помогают отображать данные поставщика. Например: -Provider сохранит список отображаемых вами элементов. Ваш виджет с отслеживанием состояния будет отслеживать выбранный в данный момент элемент - Провайдер сохраняет отображаемый текст. Ваш виджет с отслеживанием состояния сохраняет размер шрифта, цвет шрифта и т. Д., Позволяя пользователю изменять его на этом конкретном виджете, но не на всех виджетах, использующих одни и те же данные.

В вашем примере - у вас в облаке есть несколько виджетов входа на экран (по какой-то причине). В этом случае: -Если вы хотите, чтобы весь виджет входа в систему менял цвет, оставьте isGreen в классе вашего провайдера. В этом случае ваш виджет может не иметь состояния. -Если вы хотите, чтобы только виджет, по которому вы щелкнули, изменил цвет - это принадлежит вашему виджету с отслеживанием состояния, так как это значение никого не интересует.

Позвольте мне обновить это с помощью кода, который должен делать то, что вы хотите. Подключение виджета к магазину работает одинаково для виджетов Stateful и Steteless.

Примечание. Я закомментировал ваш код подключения к магазину, просто чтобы показать, как ваш виджет изменит цвет. Раскомментируйте код магазина, и все готово.

Вы можете быстро запустить это в https://dartpad.dev/, просто скопируйте / вставьте код.

// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: LoginPage(),
    );
  }
}

class LoginPage extends StatefulWidget {
  final String TAG = "LoginPage";
  LoginPage({Key key}) : super(key: key);
  
  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  
  bool isGreen = false;

  void changeColor() {
    setState(() {
      isGreen = !isGreen;
    });
    
  }

  @override
  Widget build(BuildContext context) {
    /// [StoreConnector] is used to convert store data (using the fromStore)
    /// into a ViewModel suitable for the page.
    //return StoreConnector<AppState, LoginPageViewModel>(
        //builder: (context, viewModel) {
          return Scaffold(
              body: Column(
               children: <Widget>[
                Container(),
                Text(
                  'viewModel.some_value_from_the_store',
                  style: TextStyle(color: isGreen ? Colors.green : Colors.red),
                ),
                ElevatedButton(
                  onPressed: () => changeColor(),
                  child: Text('Press to change color'),
                )
            ],
          ));
        //},
        //converter: LoginPageViewModel.fromStore);
  //}
}
}
person Andrija    schedule 24.01.2021
comment
Спасибо за объяснение, оно помогло мне понять то, что я уже смутно знал. однако я все еще борюсь с реализацией. Могу ли я использовать весь виджет LoginPage как виджет с отслеживанием состояния, при этом сохраняя его подключенным к магазину? базовая структура классов отличается от виджетов без состояния, и я не могу понять, как это построить. - person Maoration; 24.01.2021
comment
Я отредактировал свой пост с помощью кода, чтобы показать, как будет выглядеть ваш виджет с отслеживанием состояния. Я также заметил, что я имею в виду хранилище данных как Provider - поскольку я привык работать с пакетом Provider; но то же самое должно работать с Redux. - person Andrija; 24.01.2021