Flutter:为什么在构建函数中基于条件语句在脚手架之间进行切换,但不适用于自定义窗口小部件

问题描述 投票:0回答:1

我创建了一个自定义Scaffold,以便无论显示哪个页面,都可以轻松显示SnackBar消息,称为MScaffold。但是,当我尝试有条件地构建两个可能的MScaffold中的一个或另一个时,我会再次获得相同的MScaffold。当我用常规的Scaffold执行此操作时,页面切换正常,使我相信它与MScaffold有关。不过,我不知道为什么。我唯一能猜到的是,当您扩展Widget时,某些属性必须更改,但是我无法弄清楚为什么这会阻止MyHomePage显示不同的MScaffold(当再次显示时,不同的Scaffold就可以了。

我还尝试了最大扩展名Scaffold的尝试,但我发现只有当我override使用build方法时,它才能做到这一点。

以某种方式,当我在自定义overridebuild中使用ScaffoldStateWidgetMScaffold方法时,它将阻止父窗口小部件使用相同类型的新Widget进行自身重建。我还发现,当其中一个选项是Scaffold,另一个选项是MScaffold时,无论顺序如何,它的切换效果都很好。只有这两个选项均为MScaffold时,它才不再切换。

[似乎每次MScaffoldState每次都返回相同的build结果。

这里是实现(如果将两个MScaffold替换为Scaffold,则可以使用:]

import 'package:flutter/material.dart';
import 'PageSwitcher.dart';
import 'MScaffold.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MScaffold switch error',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'MScaffold switch error'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  void _switchPage() {
    PageSwitcher().switchPage();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return PageSwitcher().pageNum == 1
        ? MScaffold(
            appBar: AppBar(
              title: Text(widget.title),
            ),
            body: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Text(
                    'Page 1',
                  ),
                ],
              ),
            ),
            floatingActionButton: FloatingActionButton(
              onPressed: _switchPage,
              tooltip: 'Switch',
              child: Icon(Icons.refresh),
            ), // This trailing comma makes auto-formatting nicer for build methods.
          )
        : MScaffold(
            appBar: AppBar(
              title: Text(widget.title),
            ),
            body: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Text(
                    'Page 2',
                  ),
                ],
              ),
            ),
            floatingActionButton: FloatingActionButton(
              onPressed: _switchPage,
              tooltip: 'Switch',
              child: Icon(Icons.refresh),
            ), // This trailing comma makes auto-formatting nicer for build methods.
          );
  }
}

这里是PageSwitcher类,用于跟踪应用程序在哪个页面上:

class PageSwitcher{
  static final PageSwitcher _thisClass = PageSwitcher._internal();
  PageSwitcher._internal();
  factory PageSwitcher(){
    return _thisClass;
  }
  int pageNum = 1;
  void switchPage(){
    pageNum = pageNum==1?2:1;
  }
}

自定义WidgetMScaffoldScaffold的改动很小的版本,所以我不明白为什么会发生此错误。

这是MScaffold的页面:

import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

class ShowSnackBar {
  SnackBar currentSnackBar;
  int lastHideTime = -1; //in millisecondsSinceEpoch
  String _msg;
  bool isSnackBarVisible = false;
  static final _thisClass = ShowSnackBar._internal();
  ShowSnackBar._internal();
  factory ShowSnackBar() {
    return _thisClass;
  }
  ChangeNotifier showNotifier = ChangeNotifier();
  ChangeNotifier hideNotifier = ChangeNotifier();

  showText(String inputMsg) {
    _msg = inputMsg;
    currentSnackBar = SnackBar(
      content: Text(_msg));
    isSnackBarVisible = true;
    showNotifier.notifyListeners();
  }

  show(SnackBar inputSnackBar) {
    currentSnackBar = inputSnackBar;
    isSnackBarVisible = true;
    showNotifier.notifyListeners();
  }

  hide() {
    hideNotifier.notifyListeners();
  }
}

class MScaffold extends Scaffold {
  Key key;
  var appBar;
  var body;
  var floatingActionButton;
  var floatingActionButtonLocation;
  var floatingActionButtonAnimator;
  var persistentFooterButtons;
  var drawer;
  var endDrawer;
  var bottomNavigationBar;
  var bottomSheet;
  var backgroundColor;
  var resizeToAvoidBottomPadding;
  var resizeToAvoidBottomInset;
  var primary;
  var drawerDragStartBehavior;
  var extendBody;
  var extendBodyBehindAppBar;
  var drawerScrimColor;
  var drawerEdgeDragWidth;

  MScaffold({
    Key key,
    this.appBar,
    this.body,
    this.floatingActionButton,
    this.floatingActionButtonLocation,
    this.floatingActionButtonAnimator,
    this.persistentFooterButtons,
    this.drawer,
    this.endDrawer,
    this.bottomNavigationBar,
    this.bottomSheet,
    this.backgroundColor,
    this.resizeToAvoidBottomPadding,
    this.resizeToAvoidBottomInset,
    this.primary = true,
    this.drawerDragStartBehavior = DragStartBehavior.start,
    this.extendBody = false,
    this.extendBodyBehindAppBar = false,
    this.drawerScrimColor,
    this.drawerEdgeDragWidth,
  })  : assert(primary != null),
      assert(extendBody != null),
      assert(extendBodyBehindAppBar != null),
      assert(drawerDragStartBehavior != null),
      super(key: key);

  @override
  ScaffoldState createState() {
    return MScaffoldState(
      key: key,
      appBar: appBar,
      body: body,
      floatingActionButton: floatingActionButton,
      floatingActionButtonLocation: floatingActionButtonLocation,
      floatingActionButtonAnimator: floatingActionButtonAnimator,
      persistentFooterButtons: persistentFooterButtons,
      drawer: drawer,
      endDrawer: endDrawer,
      bottomNavigationBar: bottomNavigationBar,
      bottomSheet: bottomSheet,
      backgroundColor: backgroundColor,
      resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
      resizeToAvoidBottomInset: resizeToAvoidBottomInset,
      primary: primary,
      drawerDragStartBehavior: drawerDragStartBehavior,
      extendBody: extendBody,
      extendBodyBehindAppBar: extendBodyBehindAppBar,
      drawerScrimColor: drawerScrimColor,
      drawerEdgeDragWidth: drawerEdgeDragWidth,
    );
  }
}

class MScaffoldState extends ScaffoldState {
  Key key;
  var appBar;
  var body;
  var floatingActionButton;
  var floatingActionButtonLocation;
  var floatingActionButtonAnimator;
  var persistentFooterButtons;
  var drawer;
  var endDrawer;
  var bottomNavigationBar;
  var bottomSheet;
  var backgroundColor;
  var resizeToAvoidBottomPadding;
  var resizeToAvoidBottomInset;
  var primary;
  var drawerDragStartBehavior;
  var extendBody;
  var extendBodyBehindAppBar;
  var drawerScrimColor;
  var drawerEdgeDragWidth;

  MScaffoldState({
    Key key,
    this.appBar,
    this.body,
    this.floatingActionButton,
    this.floatingActionButtonLocation,
    this.floatingActionButtonAnimator,
    this.persistentFooterButtons,
    this.drawer,
    this.endDrawer,
    this.bottomNavigationBar,
    this.bottomSheet,
    this.backgroundColor,
    this.resizeToAvoidBottomPadding,
    this.resizeToAvoidBottomInset,
    this.primary = true,
    this.drawerDragStartBehavior = DragStartBehavior.start,
    this.extendBody = false,
    this.extendBodyBehindAppBar = false,
    this.drawerScrimColor,
    this.drawerEdgeDragWidth,
  })  : assert(primary != null),
      assert(extendBody != null),
      assert(extendBodyBehindAppBar != null),
      assert(drawerDragStartBehavior != null);

  Function() _listenerShow;
  Function() _listenerHide;

  @override
  void initState() {
    super.initState();
    _listenerShow = () {
      if (mounted) {
        Scaffold.of(_scaffoldContext)
          .showSnackBar(ShowSnackBar().currentSnackBar)
          .closed
          .then((SnackBarClosedReason reason) {
          ShowSnackBar().isSnackBarVisible = false;
          ShowSnackBar().lastHideTime = DateTime.now().millisecondsSinceEpoch;
        });
      }
    };

    _listenerHide = () {
      if (mounted) {
        Scaffold.of(_scaffoldContext).hideCurrentSnackBar();
      }
    };

    Future.microtask(() {
      if (ShowSnackBar().isSnackBarVisible) _listenerShow();
      ShowSnackBar().showNotifier.addListener(_listenerShow);
      ShowSnackBar().hideNotifier.addListener(_listenerHide);
    });
  }

  @override
  dispose() {
    ShowSnackBar().showNotifier?.removeListener(_listenerShow);
    ShowSnackBar().hideNotifier?.removeListener(_listenerHide);
    super.dispose();
  }

  BuildContext _scaffoldContext;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: key,
      appBar: appBar,
      body: Builder(
        builder: (context) {
          _scaffoldContext = context;
          return body;
        },
      ),
      floatingActionButton: floatingActionButton,
      floatingActionButtonLocation: floatingActionButtonLocation,
      floatingActionButtonAnimator: floatingActionButtonAnimator,
      persistentFooterButtons: persistentFooterButtons,
      drawer: drawer,
      endDrawer: endDrawer,
      bottomNavigationBar: bottomNavigationBar,
      bottomSheet: bottomSheet,
      backgroundColor: backgroundColor,
      resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
      resizeToAvoidBottomInset: resizeToAvoidBottomInset,
      primary: primary,
      drawerDragStartBehavior: drawerDragStartBehavior,
      extendBody: extendBody,
      extendBodyBehindAppBar: extendBodyBehindAppBar,
      drawerScrimColor: drawerScrimColor,
      drawerEdgeDragWidth: drawerEdgeDragWidth,
    );
  }
}

如您所见,MScaffold接受与Scaffold相同的参数,并使用这些参数生成Scaffold。唯一不同的是使用ShowSnackBar类来处理SnackBar消息。除此之外,它只是使用Scaffold参数,然后使用这些参数构建Scaffold


SOLUTION

解决方案是Saed Nabil的建议,加上MScaffold({Key key...更改为MScaffold({this.key...MScaffoldState({Key key...更改为MScaffoldState({this.key...,因为它不接受密钥。

新演示文稿看起来像这样:

import 'package:flutter/material.dart';
import 'PageSwitcher.dart';
import 'MScaffold.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MScaffold switch error',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'MScaffold switch error'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  void _switchPage() {
    PageSwitcher().switchPage();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return PageSwitcher().pageNum == 1
        ? MScaffold(
      key: ValueKey(1),
            appBar: AppBar(
              title: Text(widget.title),
            ),
            body: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Text(
                    'Page 1',
                  ),
                ],
              ),
            ),
            floatingActionButton: FloatingActionButton(
              onPressed: _switchPage,
              tooltip: 'Switch',
              child: Icon(Icons.refresh),
            ),
          )
        : MScaffold(
      key:ValueKey(2),
            appBar: AppBar(
              title: Text(widget.title),
            ),
            body: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Text(
                    'Page 2',
                  ),
                ],
              ),
            ),
            floatingActionButton: FloatingActionButton(
              onPressed: _switchPage,
              tooltip: 'Switch',
              child: Icon(Icons.refresh),
            ),
          );
  }
}

新的MScaffold.dart看起来像这样:

import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

class ShowSnackBar {
  SnackBar currentSnackBar;
  int lastHideTime = -1; //in millisecondsSinceEpoch
  String _msg;
  bool isSnackBarVisible = false;
  static final _thisClass = ShowSnackBar._internal();
  ShowSnackBar._internal();
  factory ShowSnackBar() {
    return _thisClass;
  }
  ChangeNotifier showNotifier = ChangeNotifier();
  ChangeNotifier hideNotifier = ChangeNotifier();

  showText(String inputMsg) {
    _msg = inputMsg;
    currentSnackBar = SnackBar(
      content: Text(_msg));
    isSnackBarVisible = true;
    showNotifier.notifyListeners();
  }

  show(SnackBar inputSnackBar) {
    currentSnackBar = inputSnackBar;
    isSnackBarVisible = true;
    showNotifier.notifyListeners();
  }

  hide() {
    hideNotifier.notifyListeners();
  }
}

class MScaffold extends Scaffold {
  Key key;
  var appBar;
  var body;
  var floatingActionButton;
  var floatingActionButtonLocation;
  var floatingActionButtonAnimator;
  var persistentFooterButtons;
  var drawer;
  var endDrawer;
  var bottomNavigationBar;
  var bottomSheet;
  var backgroundColor;
  var resizeToAvoidBottomPadding;
  var resizeToAvoidBottomInset;
  var primary;
  var drawerDragStartBehavior;
  var extendBody;
  var extendBodyBehindAppBar;
  var drawerScrimColor;
  var drawerEdgeDragWidth;

  MScaffold({
    this.key,
    this.appBar,
    this.body,
    this.floatingActionButton,
    this.floatingActionButtonLocation,
    this.floatingActionButtonAnimator,
    this.persistentFooterButtons,
    this.drawer,
    this.endDrawer,
    this.bottomNavigationBar,
    this.bottomSheet,
    this.backgroundColor,
    this.resizeToAvoidBottomPadding,
    this.resizeToAvoidBottomInset,
    this.primary = true,
    this.drawerDragStartBehavior = DragStartBehavior.start,
    this.extendBody = false,
    this.extendBodyBehindAppBar = false,
    this.drawerScrimColor,
    this.drawerEdgeDragWidth,
  })  : assert(primary != null),
      assert(extendBody != null),
      assert(extendBodyBehindAppBar != null),
      assert(drawerDragStartBehavior != null),
  super(key:key);

  @override
  ScaffoldState createState() {
    return MScaffoldState(
      key: key,
      appBar: appBar,
      body: body,
      floatingActionButton: floatingActionButton,
      floatingActionButtonLocation: floatingActionButtonLocation,
      floatingActionButtonAnimator: floatingActionButtonAnimator,
      persistentFooterButtons: persistentFooterButtons,
      drawer: drawer,
      endDrawer: endDrawer,
      bottomNavigationBar: bottomNavigationBar,
      bottomSheet: bottomSheet,
      backgroundColor: backgroundColor,
      resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
      resizeToAvoidBottomInset: resizeToAvoidBottomInset,
      primary: primary,
      drawerDragStartBehavior: drawerDragStartBehavior,
      extendBody: extendBody,
      extendBodyBehindAppBar: extendBodyBehindAppBar,
      drawerScrimColor: drawerScrimColor,
      drawerEdgeDragWidth: drawerEdgeDragWidth,
    );
  }
}

class MScaffoldState extends ScaffoldState {
  Key key;
  var appBar;
  var body;
  var floatingActionButton;
  var floatingActionButtonLocation;
  var floatingActionButtonAnimator;
  var persistentFooterButtons;
  var drawer;
  var endDrawer;
  var bottomNavigationBar;
  var bottomSheet;
  var backgroundColor;
  var resizeToAvoidBottomPadding;
  var resizeToAvoidBottomInset;
  var primary;
  var drawerDragStartBehavior;
  var extendBody;
  var extendBodyBehindAppBar;
  var drawerScrimColor;
  var drawerEdgeDragWidth;

  MScaffoldState({
    this.key,
    this.appBar,
    this.body,
    this.floatingActionButton,
    this.floatingActionButtonLocation,
    this.floatingActionButtonAnimator,
    this.persistentFooterButtons,
    this.drawer,
    this.endDrawer,
    this.bottomNavigationBar,
    this.bottomSheet,
    this.backgroundColor,
    this.resizeToAvoidBottomPadding,
    this.resizeToAvoidBottomInset,
    this.primary = true,
    this.drawerDragStartBehavior = DragStartBehavior.start,
    this.extendBody = false,
    this.extendBodyBehindAppBar = false,
    this.drawerScrimColor,
    this.drawerEdgeDragWidth,
  })  : assert(primary != null),
      assert(extendBody != null),
      assert(extendBodyBehindAppBar != null),
      assert(drawerDragStartBehavior != null);

  Function() _listenerShow;
  Function() _listenerHide;

  @override
  void initState() {
    super.initState();
    _listenerShow = () {
      if (mounted) {
        Scaffold.of(_scaffoldContext)
          .showSnackBar(ShowSnackBar().currentSnackBar)
          .closed
          .then((SnackBarClosedReason reason) {
          ShowSnackBar().isSnackBarVisible = false;
          ShowSnackBar().lastHideTime = DateTime.now().millisecondsSinceEpoch;
        });
      }
    };

    _listenerHide = () {
      if (mounted) {
        Scaffold.of(_scaffoldContext).hideCurrentSnackBar();
      }
    };

    Future.microtask(() {
      if (ShowSnackBar().isSnackBarVisible) _listenerShow();
      ShowSnackBar().showNotifier.addListener(_listenerShow);
      ShowSnackBar().hideNotifier.addListener(_listenerHide);
    });
  }

  @override
  dispose() {
    ShowSnackBar().showNotifier?.removeListener(_listenerShow);
    ShowSnackBar().hideNotifier?.removeListener(_listenerHide);
    super.dispose();
  }

  BuildContext _scaffoldContext;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: key,
      appBar: appBar,
      body: Builder(
        builder: (context) {
          _scaffoldContext = context;
          return body;
        },
      ),
      floatingActionButton: floatingActionButton,
      floatingActionButtonLocation: floatingActionButtonLocation,
      floatingActionButtonAnimator: floatingActionButtonAnimator,
      persistentFooterButtons: persistentFooterButtons,
      drawer: drawer,
      endDrawer: endDrawer,
      bottomNavigationBar: bottomNavigationBar,
      bottomSheet: bottomSheet,
      backgroundColor: backgroundColor,
      resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
      resizeToAvoidBottomInset: resizeToAvoidBottomInset,
      primary: primary,
      drawerDragStartBehavior: drawerDragStartBehavior,
      extendBody: extendBody,
      extendBodyBehindAppBar: extendBodyBehindAppBar,
      drawerScrimColor: drawerScrimColor,
      drawerEdgeDragWidth: drawerEdgeDragWidth,
    );
  }
}

而且有效!

flutter dart
1个回答
0
投票

这很麻烦,因为没有为您的MScaffold类型的有状态小部件提供键

您可以在构建这样的支架对象时通过提供适当的键来解决此问题

    MScaffold(
      key: ValueKey('1'),
      ...
)

    MScaffold(
      key: ValueKey('2'),
      ...
)
© www.soinside.com 2019 - 2024. All rights reserved.