【问题标题】:How to use two global keys for two widgets sharing the same state class如何为共享相同状态类的两个小部件使用两个全局键
【发布时间】:2020-03-02 16:56:23
【问题描述】:

我正在尝试在颤振中创建自己的自定义细分。该部分有两个按钮,一个用于教师,另一个用于学生。我正在尝试做的是将按钮封装在一个有状态的小部件中以处理两个按钮的 setState,因为我希望按钮成为 AnimatedContainer 并且如果我从父级重建子级(按钮),则转换不会有效。

请注意,按钮是堆栈定位的,我重新排序内容以使点击按钮位于另一个之上(当我在点击按钮中设置更多宽度时,这将生效,现在尚未创建)。

这是我的代码:

import 'package:flutter/cupertino.dart';

import '../../app_localizations.dart';
import '../../styles.dart';

GlobalKey<_ButtonState> teachersButtonKey = GlobalKey();
GlobalKey<_ButtonState> studentsButtonKey = GlobalKey();
String _globalTappedButtonId = 'teachersButton';

class FiltersAppBarSegment extends StatefulWidget {
  @override
  _FiltersAppBarSegmentState createState() => _FiltersAppBarSegmentState();
}

class _FiltersAppBarSegmentState extends State<FiltersAppBarSegment> {
  List<Widget> buildStackChildren(SegmentChangedCallBack handleSegmentChanged) {
    if (_globalTappedButtonId == 'teachersButton') {
      return <Widget>[
        Container(
          key: UniqueKey(),
          child: _Button(
            key: studentsButtonKey,
            id: 'studentsButton',
            label: 'seeStudents',
            rightPosition: 1,
            onSegmentChanged: handleSegmentChanged,
          ),
        ),
        Container(
          key: UniqueKey(),
          child: _Button(
            key: teachersButtonKey,
            id: 'teachersButton',
            label: 'amTeacher',
            rightPosition: null,
            onSegmentChanged: handleSegmentChanged,
          ),
        ),
      ];
    } else {
      return <Widget>[
        Container(
          key: UniqueKey(),
          child: _Button(
            key: driverButtonKey,
            id: 'driverButton',
            label: 'amDriver',
            rightPosition: null,
            onSegmentChanged: handleSegmentChanged,
          ),
        ),
        Container(
          key: UniqueKey(),
          child: _Button(
            key: studentsButtonKey,
            id: 'studentButton',
            label: 'amStudent',
            rightPosition: 1,
            onSegmentChanged: handleSegmentChanged,
          ),
        ),
      ];
    }
  }

  void handleSegmentChanged(String clickedButtonId) {
    teachersButtonKey.currentState._handleButtonTapped();
    studentsButtonKey.currentState._handleButtonTapped();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 42,
      padding: EdgeInsets.symmetric(horizontal: 20),
      child: Stack(children: buildStackChildren(handleSegmentChanged)),
    );
  }
}

class _Button extends StatefulWidget {
  final String id;
  final String label;
  final double rightPosition;
  final void onSegmentChanged;

  _Button({
    Key key,
    this.id,
    this.label,
    this.rightPosition,
    this.onSegmentChanged,
  }) : super(key: key);

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

class _ButtonState extends State<_Button> {
  bool _tapped;
  double _topPosition;
  double _width;
  double _height;

  double _getTopPosition() => _tapped ? 0 : 5;
  double _getHeight() => _tapped ? 42 : 32;

  Gradient _getGradient() {
    if (_tapped) {
      return Styles.darkAccentColorGradient;
    } else {
      return Styles.darkAccentColorGradientDisabled;
    }
  }

  void _handleButtonTapped() {
    setState(() {
      _globalTappedButtonId = widget.id;
      _tapped = (widget.id == _globalTappedButtonId);
      _topPosition = _getTopPosition();
      _height = _getHeight();
    });
  }

  @override
  void initState() {
    super.initState();

    _tapped = (widget.id == _globalTappedButtonId);
    _topPosition = _getTopPosition();
    _height = _getHeight();
  }

  @override
  Widget build(BuildContext context) {
    return Positioned(
      top: _topPosition,
      right: widget.rightPosition,
      child: GestureDetector(
        onTap: () {
          widget.onSegmentChanged('test');
        },
        child: AnimatedContainer(
          duration: Duration(seconds: 1),
          curve: Curves.fastOutSlowIn,
          width: _width,
          height: _height,
          decoration: BoxDecoration(
            gradient: _getGradient(),
            borderRadius: BorderRadius.circular(13),
          ),
          child: Center(
            child: Text(
              AppLocalizations.of(context).translate(widget.label),
              style: Styles.bodyWhiteText,
              textAlign: TextAlign.center,
            ),
          ),
        ),
      ),
    );
  }
}

【问题讨论】:

    标签: flutter dart statefulwidget


    【解决方案1】:

    我确定您现在已经找到了解决问题的方法,但是这个问题是查看此错误时的第一个搜索结果。

    如您所知,根据 GlobalKey 上的 Flutter doc

    “您不能同时在树中包含两个小部件 相同的全局键。尝试这样做会在运行时断言。”

    您可以定义自己的个人键,例如:

    import 'package:flutter/widgets.dart';
    
    class TestKeys{
      static final testKey1 = const Key('__TESTKEY1__');
      static final testKey2 = const Key('__TESTKEY2__');
      ...
    }
    

    然后在小部件中使用key: TestKeys.testKey1 引用它们

    这在 this question here 中有所描述,因此也许它可以帮助需要类似用例的人。

    也列出了一些解决方案in this GitHub issue

    【讨论】:

    • 但是您如何才能掌握当前状态呢?键没有 currentState 属性
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-15
    • 2017-06-22
    • 2012-09-20
    相关资源
    最近更新 更多