StatefulWidget как правильно инициализировать виджет
Как правильно инициализировать виджет? 1 вариант, используя initState(), далее полный пример
main.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Name App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(),
body: MyHomePage(),
),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Color _col;
String _str1;
String _str2;
bool _b;
//вызывается один раз при инициализации виджета
@override
void initState() {
super.initState();
_str1 = 'ru';
_str2 = 'Hello World!';
_col = Colors.pink;
_b = false;
}
void _f1() {
setState(() {
if(_b) {
_str1 = 'ru';
_str2 = 'Hello World!';
_col = Colors.pink;
_b = false;
} else {
_b = true;
_col = Colors.green;
_str2 = 'Привет Мир!';
_str1 = 'en';
}
});
}
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Align(
alignment: Alignment.topCenter,
child: Container(
width: double.maxFinite,
margin: const EdgeInsets.all(15.0),
child: CupertinoButton(
onPressed: () {
_f1();
},
color: Colors.black38,
borderRadius: BorderRadius.all(
Radius.circular(25.0)
),
child: Text(
_str1,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 22.0,
),
),
),
),
),
Align(
alignment: Alignment.center,
child: Container(
width: 250.0,
height: 150.0,
color: _col,
alignment: Alignment.center,
child: Text(
_str2,
style: TextStyle(
color: Colors.white,
fontSize: 36.0
),
),
),
),
],
);
}
}
2 вариант, используя только переменные, далее полный пример
main.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Name App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(),
body: MyHomePage(),
),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Color _col = Colors.pink;
String _str1 = 'ru';
String _str2 = 'Hello World!';
bool _b = false;
void _f1() {
setState(() {
if(_b) {
_str1 = 'ru';
_str2 = 'Hello World!';
_col = Colors.pink;
_b = false;
} else {
_b = true;
_col = Colors.green;
_str2 = 'Привет Мир!';
_str1 = 'en';
}
});
}
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Align(
alignment: Alignment.topCenter,
child: Container(
width: double.maxFinite,
margin: const EdgeInsets.all(15.0),
child: CupertinoButton(
onPressed: () {
_f1();
},
color: Colors.black38,
borderRadius: BorderRadius.all(
Radius.circular(25.0)
),
child: Text(
_str1,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 22.0,
),
),
),
),
),
Align(
alignment: Alignment.center,
child: Container(
width: 250.0,
height: 150.0,
color: _col,
alignment: Alignment.center,
child: Text(
_str2,
style: TextStyle(
color: Colors.white,
fontSize: 36.0
),
),
),
),
],
);
}
}
Ответы (1 шт):
В данном примере разницы лично я не вижу, но и использовать так бы я не стал.
1 вариант (InitState): Из названия понятно что оно инициализирует состояния чего либо, следовательно для этого его и нужно использовать. Также оно вызывается один раз когда этот виджет вставляется в дерево виджетов.
2 вариант: Следует использовать в связке с final или const, например const Color _col = Colors.pink;, так как такие переменные должны быть неизменяемые (у них не должно быть состояния). Либо для объектов не связанных с состоянием (например какой либо запрос), оно будет инициализироваться при вызове (new) MyHomePage().
Для того чтобы использовать StatelessWidget и иметь возможность обновлять данные (callback), мы можем поступить следующим образом:
- Использовать Stream (Rx)
- Использовать Provider
Так как Stream довольно сложные для вас, будем использовать Provider:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => MyState()),
],
child: Consumer<MyState>(
builder: (context, state, _) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Name App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(),
body: MyHomePage(state),
),
);
},
),
);
}
}
class MyHomePage extends StatelessWidget {
final MyState counterState;
const MyHomePage(this.counterState, {Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Align(
alignment: Alignment.topCenter,
child: Container(
width: double.maxFinite,
margin: const EdgeInsets.all(15.0),
child: CupertinoButton(
onPressed: () => counterState.f1(),
color: Colors.black38,
borderRadius: BorderRadius.all(Radius.circular(25.0)),
child: Text(
counterState.str1,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 22.0,
),
),
),
),
),
Align(
alignment: Alignment.center,
child: Container(
width: 250.0,
height: 150.0,
color: counterState.col,
alignment: Alignment.center,
child: Text(
counterState.str2,
style: TextStyle(color: Colors.white, fontSize: 36.0),
),
),
),
],
);
}
}
class MyState with ChangeNotifier {
// init
Color col = Colors.pink;
String str1 = 'ru';
String str2 = 'Hello World!';
bool b = false;
// update
void f1() {
if (b) {
str1 = 'ru';
str2 = 'Hello World!';
col = Colors.pink;
b = false;
} else {
b = true;
col = Colors.green;
str2 = 'Привет Мир!';
str1 = 'en';
}
notifyListeners();
}
}
Вообще необходимо максимально отказаться от использования StatefulWidget, так как это сильно трудозатратный виджет для отрисовки и обновления его. А если нет возможности (например необходим dispose()) то не использовать setState(). А чтобы заменить StatefulWidget нужно использовать StatelessWidget с конструктором и каким либо callback'ом.