【问题标题】:Flutter Web causing state reset when invoking media queryFlutter Web 在调用媒体查询时导致状态重置
【发布时间】:2021-10-08 21:11:20
【问题描述】:

看看...

文件: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());
  }
}

文件:episode.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 的新手,所以也许我必须以错误的方式做事。这是什么原因?我该如何解决?

根据一位同事的建议,将所有内容更改为有状态,我将文件 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());
  }
}

但我仍然面临同样的问题。

【问题讨论】:

  • MediaQuery 不会重置任何内容...调整屏幕大小会调用再次使用的每个构建方法以获取新的屏幕值。 EpisodesWidget() 它被一遍又一遍地创建,使其无法保持状态。你需要改变你的逻辑或者让一切都变成有状态的。
  • LayoutBuilder代替怎么样?
  • @YeasinSheikh,此代码是场景的最小版本 - 仅用于说明问题。整个项目都有很多这样的情况。所以我正在寻找解决方案或者解释。不过谢谢!无论如何,我都会研究 Layour Builder。
  • 我明白,不用他居高临下。每次调用 build 方法时都会调用 MediaQuery,这就是继承小部件的工作方式,调用 MediaQuery 内部的变量将请求新的窗口大小信息并通知渲染树。我建议您阅读 Flutter 渲染树的工作原理。
  • @MarianoZorrilla 您的第一条评论不正确。问题是他们忽略了分析器警告并试图将状态存储在StatefulWidget 而不是State 中。无需更改任何逻辑并使任何其他小部件有状态。仅仅因为 EpisodesWidget 被一遍又一遍地创建并不意味着它的状态正在被破坏。

标签: flutter dart flutter-web


【解决方案1】:

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

【讨论】:

  • 谢谢!是的,你是对的。正如我提到的,我是 Flutter 的新手。所以这可能是我做错的事情,一些概念性的事情。谢谢!
  • @MarianoZorrilla 没有必要有毒。对于什么是“逻辑”,我们可能有不同的定义。 only “问题”是他们在错误的范围内声明了一个变量,我个人认为这与逻辑相去甚远。我还对您的声明提出了异议,即问题存在于 UniqueKey 的某个地方,并且您在第一条评论中暗示一遍又一遍地创建一个小部件在某种程度上是 Flutter 中的一个问题并且正在破坏状态,这绝对不是。我建议阅读有关小部件和元素树以及它们如何交互的信息。
猜你喜欢
  • 2019-10-09
  • 2021-03-25
  • 1970-01-01
  • 2018-05-12
  • 1970-01-01
  • 2020-06-18
  • 1970-01-01
  • 2013-04-11
  • 2011-11-29
相关资源
最近更新 更多