在State类之外更改窗口小部件的状态

时间:2018-12-13 15:34:46

标签: dart flutter

我是Flutter开发的初学者,遇到了一些问题。我会尽力向我解释。

我有一个滑动条(有状态的自定义窗口小部件)。我想将此小部件集成到我的主视图中,但是我必须更改滑块的值(此值也可以通过轻击手势检测器更改)。

经过网上研究,我尝试了几件事。
例如,使用VerticalSliderState的吸气剂并访问VerticalSliderState方法来更改值。
在收到the state is null错误后,这种方法只起作用了一次,我想我错过了setState()的抖动概念。
有什么解释吗?谢谢:P

这是我的代码:

有状态的自定义小部件:

import 'package:flutter/material.dart';

class VerticalSlider extends StatefulWidget {
  ....
  final double value;
  .....
  final void Function(double) onValueChanged;

   VerticalSlider({
    Key key,
    @required this.height,
    @required this.width,
    this.onValueChanged,
    this.value,
    ....
  }) : super(key: key);


  VerticalSliderState state;

  @override
  VerticalSliderState createState(){
    state = new VerticalSliderState();
    return state ;
  }
}

class VerticalSliderState extends State<Vertical Slider>{
  .....
double _value;

  double _currentHeight;
  Widget _movingDecoratedBox;
  Widget _fixedDecoratedBox;

  void setValue(double value){
    _setValue(value, false, widget.height);
  }

  initState() {
    super.initState();
    _value = widget.value ?? 5.0;
    _currentHeight = _convertValueToHeight();
    _movingDecoratedBox = widget.movingBox ?? DecoratedBox(
        decoration: BoxDecoration(color: Colors.red)
    );
    _fixedDecoratedBox = widget.fixedBox ?? DecoratedBox(
      decoration: BoxDecoration(color: Colors.grey),
    );
  }

  .......... 

  void _onTapUp(TapUpDetails tapDetails) {
    RenderBox renderBox = context.findRenderObject();
    var newHeight = widget.height - renderBox.globalToLocal(tapDetails.globalPosition).dy;
    var newValue = _convertHeightToValue(newHeight);
    setState(() {
      _currentHeight = (widget.height/10.5) * (newValue);
      _setValue(newValue, true, widget.height);

    });

  }

  void _setValue(double newValue, bool userRequest, double height) {
    _value = newValue;
    if(userRequest){
      widget.onValueChanged(_value);
    }
    else{
      setState(() {
        _currentHeight = (height/10.5) * (newValue);
      });
    }
  }

Widget _buildMovingBox() {
    return Align(
      alignment: Alignment.bottomCenter,
      child: SizedBox(
        width: widget.width,
        height: _currentHeight,
        child: _movingDecoratedBox,
      ),
    );
  }
  .......... 

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapUp: _onTapUp,
      child: Stack(
        alignment: AlignmentDirectional.bottomCenter,
        children: <Widget>[
          _buildFixedBox(),
          _buildMovingBox(),
        ],
      ),
    );
  }

我的主视图:

seekBar1 = new VerticalSlider(
      height: mediaQueryData.size.height /  2.7,
      width: 40.0,
      max: 11.0,
      min: 0.0,
      value: 5.5,
      movingBox: new Container(color: Colors.teal),
      fixedBox: new Container(color: Colors.grey[200]),
      onValueChanged: onValueChanged1,
    );

void onValueChanged1(double newValue) {
      seekBar2.state.setValue(10-newValue);
      print(newValue);
  }

2 个答案:

答案 0 :(得分:0)

请记住以下几点:

  • 您不想在state中使用Stateful Widget变量, 您都不想通过myWidget.state访问它。让 小部件本身会处理自己的状态。

  • 您得到null的原因是,当您在自己的计算机上执行setStateVerticalSliderState对象,它将重建您的树,因此, 生成新的更新后的VerticalSliderState。那意味着你 状态引用现在将为空。

  • 如果您想访问自己州内的某些数据,可以采取 最终回调(例如您的onValueChanged)的优势或 可以使用Streams

  • 此外,使用方法返回Widgets而不是变量,这更正确。

  • 例如,如果您有seekBar2.state.setValue(10-newValue);,则只想创建一个具有更新值的新seekBar2 = VerticalSlider(updated properties),而不要这样做。

答案 1 :(得分:0)

一个解决方案是下面完整示例中的解决方案。滑块小部件将更新主页中的值,并用于设置另一个滑块中的值。

由于您的代码段不完整,我已经编写了一些代码。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> {
  double _value = 9;

  void onValueChanged(double newValue) {
    setState(() {
      _value = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Test"),
      ),
      body: new Center(
        child: new Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            VerticalSlider(
              height: MediaQuery.of(context).size.height / 2.7,
              width: 40.0,
              max: 11.0,
              min: 0.0,
              value: _value,
              movingBox: new Container(color: Colors.teal),
              fixedBox: new Container(color: Colors.grey[200]),
              onValueChanged: onValueChanged,
            ),
            SizedBox(
              width: 50.0,
            ),
            VerticalSlider(
              height: MediaQuery.of(context).size.height / 2.7,
              width: 40.0,
              max: 11.0,
              min: 0.0,
              value: _value,
              movingBox: new Container(color: Colors.teal),
              fixedBox: new Container(color: Colors.grey[200]),
              onValueChanged: onValueChanged,
            ),
          ],
        ),
      ),
    );
  }
}

class VerticalSlider extends StatefulWidget {
  final double height;
  final double width;
  final double max;
  final double min;
  final double value;
  final Widget movingBox;
  final Widget fixedBox;
  final void Function(double) onValueChanged;

  VerticalSlider({
    Key key,
    @required this.height,
    @required this.width,
    this.onValueChanged,
    this.value,
    this.max,
    this.min,
    this.movingBox,
    this.fixedBox,
  }) : super(key: key);

  @override
  VerticalSliderState createState() {
    return VerticalSliderState();
  }
}

class VerticalSliderState extends State<VerticalSlider> {
  double _value;
  double _currentHeight;

  @override
  void didUpdateWidget(VerticalSlider oldWidget) {
    super.didUpdateWidget(oldWidget);
    _init();
  }

  initState() {
    super.initState();
    _init();
  }

  void _init() {
    _value = widget.value ?? widget.max / 2;
    _currentHeight = _convertValueToHeight();
  }

  double _convertValueToHeight() {
    return _value * widget.height / widget.max;
  }

  double _convertHeightToValue(double height) {
    return height * widget.max / widget.height;
  }

  void _onTapUp(TapUpDetails tapDetails) {
    RenderBox renderBox = context.findRenderObject();
    var newHeight =
        widget.height - renderBox.globalToLocal(tapDetails.globalPosition).dy;
    var newValue = _convertHeightToValue(newHeight);
    widget.onValueChanged(newValue);
    setState(() {
      _currentHeight = newHeight;
    });
  }

  Widget _buildMovingBox() {
    return Align(
      alignment: Alignment.bottomCenter,
      child: SizedBox(
        width: widget.width,
        height: _currentHeight,
        child: widget.movingBox ??
            DecoratedBox(decoration: BoxDecoration(color: Colors.red)),
      ),
    );
  }

  Widget _buildFixedBox() {
    return Align(
      alignment: Alignment.bottomCenter,
      child: SizedBox(
        width: widget.width / 2,
        height: double.infinity,
        child: widget.fixedBox ??
            DecoratedBox(
              decoration: BoxDecoration(color: Colors.grey),
            ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapUp: _onTapUp,
      child: SizedBox(
        width: widget.width,
        height: widget.height,
        child: Stack(
          alignment: AlignmentDirectional.bottomCenter,
          children: <Widget>[
            _buildFixedBox(),
            _buildMovingBox(),
          ],
        ),
      ),
    );
  }
}