Передача данных в виджет с отслеживанием состояния
Мне интересно, каков наилучший / наиболее эффективный способ передачи данных в виджет с сохранением состояния при его создании.
Два стиля, которые я видел:
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) - это более приятная и чистая замена для
SatefullWidget
s:
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));
},);
}
}