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,
        ),
      ),
    ));
  }
}

这个例子只是一个简单的例子:当你点击它时,这将在播放和暂停图标之间切换

这确实有效,但是如果我调整屏幕大小,它会回到初始状态。

所以,一个测试是:

如果我评论这行

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 移至状态可消除此问题,因为状态与小部件本身是分开存在的。只要小部件在小部件树中持续存在,状态就会持续存在。