Передача данных в виджет с отслеживанием состояния

Мне интересно, каков наилучший / наиболее эффективный способ передачи данных в виджет с сохранением состояния при его создании.

Два стиля, которые я видел:

class ServerInfo extends StatefulWidget {

  Server _server;

  ServerInfo(Server server) {
    this._server = server;
  }

  @override
    State<StatefulWidget> createState() => new _ServerInfoState(_server);
}

class _ServerInfoState extends State<ServerInfo> {
  Server _server;

  _ServerInfoState(Server server) {
    this._server = server;
  }
}

Этот метод сохраняет значение как в ServerInfo а также _ServerInfoState, что кажется немного расточительным.

Другой метод заключается в использовании widget._server:

class ServerInfo extends StatefulWidget {

  Server _server;

  ServerInfo(Server server) {
    this._server = server;
  }

  @override
    State<StatefulWidget> createState() => new _ServerInfoState();
}

class _ServerInfoState extends State<ServerInfo> {
  @override
    Widget build(BuildContext context) {
      widget._server = "10"; // Do something we the server value
      return null;
    }
}

Это кажется немного задом наперед, так как состояние больше не сохраняется в _ServerInfoSate но вместо этого в виджете.

Есть ли лучшая практика для этого?

8 ответов

Решение

Не передавайте параметры State используя его конструктор. Вы должны получить доступ только к ним, используя this.widget.myField,

Не только редактирование конструктора требует много ручной работы; это ничего не приносит. Нет причин дублировать все поля Widget,

РЕДАКТИРОВАТЬ:

Вот пример

class MyStateful extends StatefulWidget {
  final String foo;

  const MyStateful({Key key, this.foo}): super(key: key);

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

class _MyStatefulState extends State<MyStateful> {
  @override
  Widget build(BuildContext context) {
    return Text(widget.foo);
  }
}

Лучший способ - не передавать параметры классу State с помощью его конструктора. Вы можете легко получить доступ к классу State, используяwidget.myField.

Например

class UserData extends StatefulWidget {
  final String clientName;
  final int clientID;
  const UserData(this.clientName,this.clientID);

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

class UserDataState extends State<UserData> {
  @override
  Widget build(BuildContext context) {
    // Here you direct access using widget
    return Text(widget.clientName); 
  }
}

Передайте свои данные при навигации по экрану:

 Navigator.of(context).push(MaterialPageRoute(builder: (context) => UserData("WonderClientName",132)));

Другой ответ, основанный на ответе @RémiRousselet и на вопрос @user6638204, если вы хотите передать начальные значения и все еще иметь возможность обновлять их в состоянии позже:

class MyStateful extends StatefulWidget {
  final String foo;

  const MyStateful({Key key, this.foo}): super(key: key);

  @override
  _MyStatefulState createState() => _MyStatefulState(foo: this.foo);
}

class _MyStatefulState extends State<MyStateful> {
  String foo;

  _MyStatefulState({this.foo});

  @override
  Widget build(BuildContext context) {
    return Text(foo);
  }
}

Для передачи начальных значений (ничего не передавая конструктору)

class MyStateful extends StatefulWidget {
  final String foo;

  const MyStateful({Key key, this.foo}): super(key: key);

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

class _MyStatefulState extends State<MyStateful> {
  @override
  void initState(){
    super.initState();
    // you can use this.widget.foo here
  }

  @override
  Widget build(BuildContext context) {
    return Text(foo);
  }
}

API виджетов с отслеживанием состояния Flutter довольно неудобен: данные хранятся в виджетах, чтобы получить к ним доступ в build() метод, который находится в Stateобъект 🤦 Если вы не хотите использовать некоторые из более крупных параметров управления состоянием (Provider, BLoC), используйте flutter_hooks (https://pub.dev/packages/flutter_hooks) - это более приятная и чистая замена для SatefullWidgets:

      class Counter extends HookWidget {
  final int _initialCount;

  Counter(this._initialCount = 0);
  
  @override
  Widget build(BuildContext context) {
    final counter = useState(_initialCount);

    return GestureDetector(
      // automatically triggers a rebuild of Counter widget
      onTap: () => counter.value++,
      child: Text(counter.value.toString()),
    );
  }
}

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

      class SomePage extends StatefulWidget{
  final String? value;
  SomePage({this.value});

  @override
  State<SomePage> createState() => _SomePageState();
}

class _SomePageState extends State<SomePage> {
  String? _value;

  @override
  void initState(){
    super.initState();
    setState(() {
      _value = widget.value;
    });
  }

 @override
 Widget build(BuildContext context) {
    return Text(_value);
 }
}

@ Реми Русселе, @Sanjayrajsinh, @Daksh Shah тоже лучше. но я также определил, что это от начальной точки. что какой параметр имеет значение

   import 'package:flutter/material.dart';
    
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      String name = "Flutter Demo";
      String description = "This is Demo Application";
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: MainActivity(
            appName: name,
            appDescription: description,
          ),
        );
      }
    }
    
    class MainActivity extends StatefulWidget {
      MainActivity({Key key, this.appName, this.appDescription}) : super(key: key);
      var appName;
      var appDescription;
    
      @override
      _MainActivityState createState() => _MainActivityState();
    }
    
    class _MainActivityState extends State<MainActivity> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.appName),
          ),
          body: Scaffold(
            body: Center(
              child: Text(widget.appDescription),
            ),
          ),
        );
      }
    }

Пройти dataдля виджета с состоянием, прежде всего, создайте две страницы. Теперь с первой страницы откройте вторую страницу и передайте данные.

      class PageTwo extends StatefulWidget {
  final String title;
  final String name;

  PageTwo ({ this.title, this.name });

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

class PageTwoStateState extends State<PageTwo> {
  @override
  Widget build(BuildContext context) {
      return Text(
         widget.title,
         style: TextStyle(
               fontSize: 18, fontWeight: FontWeight.w700),
               ),
  }
}

class PageOne extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialButton(
          text: "Open PageTwo",
          onPressed: () {
                var destination = ServicePage(
                   title: '<Page Title>',
                   provider: '<Page Name>',
                );
                Navigator.push(context,
                    MaterialPageRoute(builder: (context) => destination));
                        },);
  }
}
Другие вопросы по тегам