Flutter-自定义小部件-如何从

时间:2019-05-08 14:24:24

标签: flutter widget

这是我所遇到问题的一个简单示例。给出以下示例,如何从Class外部获取“ counter”的值?

class Counter extends StatefulWidget {
  @override
  _CounterState createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int counter = 0;
  void increaseCount() {
    setState(() => this.counter++);
    print("New count = $counter");
  }

  Widget build(context) {
    return new RaisedButton(
      onPressed: increaseCount,
      child: new Text('Tap To Add'),
    );
  }
}

4 个答案:

答案 0 :(得分:0)

在Flutter中,通常要做的是传递一个回调函数,在该函数中,您可以传递所需的值,例如

class Counter extends StatefulWidget {
  // you can use a callback function
  final ValueSetter<int> callback;

  Counter({this.callback});

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

class _CounterState extends State<Counter> {
  int counter = 0;
  void increaseCount() {
    setState(() => this.counter++);
    print("New count = $counter");
    // Here you can pass the value
    widget.callback(this.counter);
  }

  Widget build(context) {
    return new RaisedButton(
      onPressed: increaseCount,
      child: new Text('Tap To Add'),
    );
  }
}

调用小部件时,请执行以下操作:

Counter(callback: (counter){
      // Here you can take some actions on counter
    });

这是我所知道的最简单的方法,也可以使用其他一些模式,例如块或其他模式。
希望有帮助。

答案 1 :(得分:0)

您可以像这样使用GlobalKey:

class Counter extends StatefulWidget {
  @override
  final globalKey = GlobalKey<_CounterState>();

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

class _CounterState extends State<Counter> {
  int counter = 0;
  void increaseCount() {
    setState(() => counter++);
    print("New count = $counter");
  }

  @override
  Widget build(context) {
    return RaisedButton(
      onPressed: increaseCount,
      child: Text('Tap To Add'),
    );
  }
}

并像这样访问计数器:

Counter counter = Counter();
int count = counter.globalKey.currentState.counter;

警告:建议

您正在从一个简单的包含状态过渡到几个小部件之间共享的状态。有几种更好的方法可以解决此问题。有关更多信息和解决问题的更好方法,请访问https://flutter.dev/docs/development/data-and-backend/state-mgmt

答案 2 :(得分:0)

这只是一个示例(概念验证),用于演示我想要实现的目标。我只是希望小部件存储其值并在需要时将其提供给我(即更新数据)。另一种方法是让主程序存储该值,但我认为这不是理想的选择。

以下内容似乎起作用。简单地获取计数器的值似乎是我要做的很多事情。我希望有一个更简单的方法。代码如下:

import 'package:flutter/material.dart';
import 'counterWithState.dart';

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

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

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

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

class _MyHomePageState extends State<MyHomePage> {
  CounterWithState _counterWithState =
      CounterWithState(iCounter: 0, isAllowedChange: true);
  FloatingActionButton _fab;
  final _scaffoldKey = GlobalKey<ScaffoldState>();
  @override
  void initState() {
    super.initState();
    _fab = FloatingActionButton(
      onPressed: _showSnackbar,
      tooltip: 'Press to show Counter',
      child: Icon(Icons.info),
    );
  }

  _showSnackbar() {
    _scaffoldKey.currentState.showSnackBar(SnackBar(
        backgroundColor: Colors.blue,
        content:
            Text("Current value of Counter is ${_counterWithState.iCounter}")));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            _counterWithState.getCounterWidget(),
          ],
        ),
      ),
      floatingActionButton: _fab,
    );
  }
}
import 'package:flutter/material.dart';

class CounterWithState {
  int _iCounter;
  CounterWithStateInternal _counterWithStateInternal;

  fnDataChanged(int iNewCounter) {
    _iCounter = iNewCounter;
    debugPrint("CounterWithState: New value = $_iCounter");
  }

  CounterWithStateInternal getCounterWidget() {
    return _counterWithStateInternal;
  }

  CounterWithState({@required iCounter, @required bool isAllowedChange}) {
    _iCounter = iCounter;
    _counterWithStateInternal = CounterWithStateInternal(
        GlobalKey(), this._iCounter, isAllowedChange, fnDataChanged);
  }
  get iCounter => _iCounter;
}

class CounterWithStateInternal extends StatefulWidget {
  final int iCounter;
  final bool tfAllowChange;
  final Function fnDataChanged;
  CounterWithStateInternal(
      Key key, this.iCounter, this.tfAllowChange, this.fnDataChanged)
      : super(key: key);
  @override
  CounterWithStateMain createState() => CounterWithStateMain();
}

class CounterWithStateMain extends State<CounterWithStateInternal> {
  int _iCounter = 0;

  @override
  initState() {
    super.initState();
    _iCounter = widget.iCounter;
  }

  void increaseCount() {
    if (widget.tfAllowChange) {
      setState(() => this._iCounter++);
      widget.fnDataChanged(_iCounter);
      print("CounterWithStateMain: New count = $_iCounter");
    }
  }

  Widget build(context) {
    return Column(children: <Widget>[
      Text("Value of counter = $_iCounter"),
      SizedBox(
        height: 20.0,
      ),
      RaisedButton(
        onPressed: increaseCount,
        child: Text('Tap To Add'),
      )
    ]);
  }
}

答案 3 :(得分:0)

这是我先前回答的更新,目的是更好地说明我想要实现的目标。我目前在代码中使用Checkbox来存储自己的值。

这只是一个示例(概念验证),用于演示我想要实现的目标。我只是希望小部件存储其值并在需要时将其提供给我(即更新数据)。另一种方法是让主程序存储该值,但我认为这不是理想的选择。

以下内容似乎起作用。简单地获取计数器的值似乎是我要做的很多事情。我希望有一个更简单的方法。代码如下:

import 'package:flutter/material.dart';
import 'counterWithState.dart';

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

class MyApp extends StatelessWidget {
  final String sTitle = 'Flutter Counter-With-State Demo';
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: sTitle,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: sTitle),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  CounterWithState _counterWithState;
  FloatingActionButton _fab;
  int _iCounterOriginal = 99;
  RaisedButton _raisedButton;
  SizedBox _sizedBox;
  final _scaffoldKey = GlobalKey<ScaffoldState>();

  @override
  void initState() {
    super.initState();
    _fab = FloatingActionButton(
      onPressed: _showSnackbar,
      tooltip: 'Press to show Counter',
      child: Icon(Icons.info),
    );
    _raisedButton = RaisedButton(
        child: const Text('Update'),
        color: Theme.of(context).accentColor,
        elevation: 4.0,
        splashColor: Colors.blueGrey,
        onPressed: () {
          _iCounterOriginal = _counterWithState.iCounter;
          _counterWithState = null;
          _getCounterWithState(context);
          setState(() {});
        });
    _sizedBox = SizedBox(height: _raisedButton.height);
  }

  fnCounterChanged(int iCounter) {
    setState(() {});
  }

  _showSnackbar() {
    _scaffoldKey.currentState.showSnackBar(SnackBar(
        backgroundColor: Colors.blue,
        content:
            Text("Current value of Counter is ${_counterWithState.iCounter}")));
  }

  @override
  Widget build(BuildContext context) {
    _counterWithState = _counterWithState != null
        ? _counterWithState
        : _getCounterWithState(context);
    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(
        title: Text(widget.title),
        centerTitle: true,
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            _counterWithState.getCounterWidget(),
            _getUpdateButton(context),
          ],
        ),
      ),
      floatingActionButton: _fab,
    );
  }

  Widget _getUpdateButton(BuildContext context) {
    return _counterWithState == null ||
            _counterWithState.iCounter == _iCounterOriginal
        ? _sizedBox
        : _raisedButton;
  }

  CounterWithState _getCounterWithState(context) {
    if (_counterWithState == null)
      _counterWithState = CounterWithState(
          iCounter: _iCounterOriginal,
          isAllowedChange: true,
          fnNotifyChange: fnCounterChanged);
    return _counterWithState;
  }
}
import 'package:flutter/material.dart';

class CounterWithState {
  int _iCounter;
  final Function fnNotifyChange;
  CounterWithStateInternal _counterWithStateInternal;

  fnDataChanged(int iNewCounter) {
    _iCounter = iNewCounter;
    if (fnNotifyChange != null) fnNotifyChange(iNewCounter);
  }

  CounterWithStateInternal getCounterWidget() {
    return _counterWithStateInternal;
  }

  CounterWithState(
      {@required iCounter,
      @required bool isAllowedChange,
      this.fnNotifyChange}) {
    _iCounter = iCounter;
    _counterWithStateInternal = CounterWithStateInternal(
        GlobalKey(), this._iCounter, isAllowedChange, fnDataChanged);
  }
  get iCounter => _iCounter;
}

class CounterWithStateInternal extends StatefulWidget {
  final int iCounter;
  final bool tfAllowChange;
  final Function fnDataChanged;
  CounterWithStateInternal(
      Key key, this.iCounter, this.tfAllowChange, this.fnDataChanged)
      : super(key: key);
  @override
  CounterWithStateMain createState() => CounterWithStateMain();
}

class CounterWithStateMain extends State<CounterWithStateInternal> {
  int _iCounter;
  int _iOriginalCounter;

  @override
  initState() {
    super.initState();
    _iCounter = widget.iCounter;
    _iOriginalCounter = widget.iCounter;
  }

  void incrementCounter(int iValue) {
    if (widget.tfAllowChange) {
      setState(() => this._iCounter += iValue);
      widget.fnDataChanged(_iCounter);
    }
  }

  Widget build(context) {
    return Column(children: <Widget>[
      Text("Value of original counter = $_iOriginalCounter"),
      _getSizedBox(),
      Text("Value of counter = $_iCounter"),
      _getSizedBox(),
      RaisedButton(
        onPressed: (() => incrementCounter(1)),
        child: Text('Tap To Add'),
      ),
      _getSizedBox(),
      RaisedButton(
        onPressed: (() => incrementCounter(-1)),
        child: Text('Tap To Subtract'),
      ),
      _getSizedBox(),
    ]);
  }

  SizedBox _getSizedBox() {
    return SizedBox(height: 20.0);
  }
}