Flutter Web 在调用媒体查询时导致状态重置
Flutter Web causing state reset when invoking media query
看看...
文件:dashboard.dart
// @dart=2.8
import 'package:flutter/material.dart';
import 'package:myApp/Pages/Dashboard/widgets/Episodes/episodes.dart';
class DashboardPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final double scrWidth = MediaQuery.of(context).size.width; // if I comment this line, this will work !!!!
return Container(child: EpisodesWidget());
}
}
文件:episodes.dart
// @dart=2.8
import 'package:flutter/material.dart';
class EpisodesWidget extends StatefulWidget {
String nowPlaying = "";
@override
_EpisodesWidgetState createState() => _EpisodesWidgetState();
}
class _EpisodesWidgetState extends State<EpisodesWidget> {
void play(String file) async {
if (file == widget.nowPlaying) {
setState(() {
widget.nowPlaying = "";
});
}
setState(() {
widget.nowPlaying = file;
});
return;
}
@override
Widget build(BuildContext context) {
return Column(children: [
RowWidget(
key: UniqueKey(),
id: '1111',
currentPlaying: widget.nowPlaying,
onPlay: (value) => play(value)),
]);
}
}
class RowWidget extends StatefulWidget {
final String id;
final String currentPlaying;
final ValueChanged onPlay;
RowWidget({this.id, this.currentPlaying, this.onPlay, Key key})
: super(key: key);
@override
_RowWidgetState createState() => _RowWidgetState();
}
class _RowWidgetState extends State<RowWidget> {
@override
Widget build(BuildContext context) {
return Container(
child: GestureDetector(
onTap: () => widget.onPlay(widget.id + '.mp3'),
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: Icon(
widget.currentPlaying == widget.id + '.mp3'
? Icons.pause_circle
: Icons.play_circle,
color: widget.currentPlaying == widget.id + '.mp3'
? Colors.grey
: Color(0xFFFF8117e),
size: 25,
),
),
));
}
}
这个例子只是一个简单的例子:当你点击它时,这将在播放和暂停图标之间切换
这确实有效,但是如果我调整屏幕大小,它会回到初始状态。
所以,一个测试是:
- 使用Chrome
执行
- 按下播放按钮(这将成为暂停按钮)
- 现在调整屏幕大小
- 暂停按钮恢复为播放按钮只是因为我调整了屏幕大小
如果我评论这行
final double scrWidth = MediaQuery.of(context).size.width;
这将按预期工作。未使用变量 scrWidth
。因此,仅使用媒体查询对此变量进行简单赋值就会导致非常奇怪的行为。
有人可以帮助我吗?这是 Flutter Bug 吗?我是 Flutter 的新手,所以也许我必须以错误的方式做事。这是什么原因?我该如何解决?
根据同事的建议将所有内容更改为有状态,我将文件 dashboard.dart 设置为:
import 'package:flutter/material.dart';
import 'package:...episodes.dart';
class DashboardPage extends StatefulWidget {
@override
_DashboardPageState createState() => _DashboardPageState();
}
class _DashboardPageState extends State<DashboardPage> {
@override
Widget build(BuildContext context) {
final double scrWidth = MediaQuery.of(context).size.width;
return Container(child: EpisodesWidget());
}
}
但我仍然面临同样的问题。
这不是 Flutter 的错误。这是您的代码的问题。您的 nowPlaying
String
实际上不是小部件状态的一部分。将其移动到状态:
class EpisodesWidget extends StatefulWidget {
//Widget state should not be stored here!
@override
_EpisodesWidgetState createState() => _EpisodesWidgetState();
}
class _EpisodesWidgetState extends State<EpisodesWidget> {
String nowPlaying = "";//Store it here!
void play(String file) async {
if (file == widget.nowPlaying) {
setState(() {
widget.nowPlaying = "";
});
}
setState(() {
widget.nowPlaying = file;
});
return;
}
@override
Widget build(BuildContext context) {
return Column(children: [
RowWidget(
key: UniqueKey(),
id: '1111',
currentPlaying: widget.nowPlaying,
onPlay: (value) => play(value)),
]);
}
}
以后请不要忽略分析器警告。您应该已经收到有关小部件 类 不可变的警告,这是由于试图在小部件本身内存储状态引起的。
当您执行 MediaQuery.of(context).size.width;
时,您正在监听应用程序屏幕宽度的变化。每次小部件更改时,您的 DashboardPage
小部件都会再次调用 build
。然后 重新创建 EpisodesWidget
和您之前存储在那里的 nowPlaying
变量。
将 nowPlaying
移至状态可消除此问题,因为状态与小部件本身是分开存在的。只要小部件在小部件树中持续存在,状态就会持续存在。
看看...
文件:dashboard.dart
// @dart=2.8
import 'package:flutter/material.dart';
import 'package:myApp/Pages/Dashboard/widgets/Episodes/episodes.dart';
class DashboardPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final double scrWidth = MediaQuery.of(context).size.width; // if I comment this line, this will work !!!!
return Container(child: EpisodesWidget());
}
}
文件:episodes.dart
// @dart=2.8
import 'package:flutter/material.dart';
class EpisodesWidget extends StatefulWidget {
String nowPlaying = "";
@override
_EpisodesWidgetState createState() => _EpisodesWidgetState();
}
class _EpisodesWidgetState extends State<EpisodesWidget> {
void play(String file) async {
if (file == widget.nowPlaying) {
setState(() {
widget.nowPlaying = "";
});
}
setState(() {
widget.nowPlaying = file;
});
return;
}
@override
Widget build(BuildContext context) {
return Column(children: [
RowWidget(
key: UniqueKey(),
id: '1111',
currentPlaying: widget.nowPlaying,
onPlay: (value) => play(value)),
]);
}
}
class RowWidget extends StatefulWidget {
final String id;
final String currentPlaying;
final ValueChanged onPlay;
RowWidget({this.id, this.currentPlaying, this.onPlay, Key key})
: super(key: key);
@override
_RowWidgetState createState() => _RowWidgetState();
}
class _RowWidgetState extends State<RowWidget> {
@override
Widget build(BuildContext context) {
return Container(
child: GestureDetector(
onTap: () => widget.onPlay(widget.id + '.mp3'),
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: Icon(
widget.currentPlaying == widget.id + '.mp3'
? Icons.pause_circle
: Icons.play_circle,
color: widget.currentPlaying == widget.id + '.mp3'
? Colors.grey
: Color(0xFFFF8117e),
size: 25,
),
),
));
}
}
这个例子只是一个简单的例子:当你点击它时,这将在播放和暂停图标之间切换
这确实有效,但是如果我调整屏幕大小,它会回到初始状态。
所以,一个测试是:
- 使用Chrome 执行
- 按下播放按钮(这将成为暂停按钮)
- 现在调整屏幕大小
- 暂停按钮恢复为播放按钮只是因为我调整了屏幕大小
如果我评论这行
final double scrWidth = MediaQuery.of(context).size.width;
这将按预期工作。未使用变量 scrWidth
。因此,仅使用媒体查询对此变量进行简单赋值就会导致非常奇怪的行为。
有人可以帮助我吗?这是 Flutter Bug 吗?我是 Flutter 的新手,所以也许我必须以错误的方式做事。这是什么原因?我该如何解决?
根据同事的建议将所有内容更改为有状态,我将文件 dashboard.dart 设置为:
import 'package:flutter/material.dart';
import 'package:...episodes.dart';
class DashboardPage extends StatefulWidget {
@override
_DashboardPageState createState() => _DashboardPageState();
}
class _DashboardPageState extends State<DashboardPage> {
@override
Widget build(BuildContext context) {
final double scrWidth = MediaQuery.of(context).size.width;
return Container(child: EpisodesWidget());
}
}
但我仍然面临同样的问题。
这不是 Flutter 的错误。这是您的代码的问题。您的 nowPlaying
String
实际上不是小部件状态的一部分。将其移动到状态:
class EpisodesWidget extends StatefulWidget {
//Widget state should not be stored here!
@override
_EpisodesWidgetState createState() => _EpisodesWidgetState();
}
class _EpisodesWidgetState extends State<EpisodesWidget> {
String nowPlaying = "";//Store it here!
void play(String file) async {
if (file == widget.nowPlaying) {
setState(() {
widget.nowPlaying = "";
});
}
setState(() {
widget.nowPlaying = file;
});
return;
}
@override
Widget build(BuildContext context) {
return Column(children: [
RowWidget(
key: UniqueKey(),
id: '1111',
currentPlaying: widget.nowPlaying,
onPlay: (value) => play(value)),
]);
}
}
以后请不要忽略分析器警告。您应该已经收到有关小部件 类 不可变的警告,这是由于试图在小部件本身内存储状态引起的。
当您执行 MediaQuery.of(context).size.width;
时,您正在监听应用程序屏幕宽度的变化。每次小部件更改时,您的 DashboardPage
小部件都会再次调用 build
。然后 重新创建 EpisodesWidget
和您之前存储在那里的 nowPlaying
变量。
将 nowPlaying
移至状态可消除此问题,因为状态与小部件本身是分开存在的。只要小部件在小部件树中持续存在,状态就会持续存在。