Сочетание Stateful и Stateless флаттера пользовательского интерфейса не обновляется - PullRequest
0 голосов
/ 29 декабря 2018

Использование виджетов с отслеживанием состояния, приводящих к отображению устаревших виджетов

У меня есть пользовательский интерфейс, который получает обновления текста по сети.Когда приходит обновление, я хочу сначала показать его красным, а затем постепенно поменять его на черный, поскольку возраст сообщения увеличивается.Если новое сообщение поступает из того же источника, я хотел заменить текст старого сообщения новым сообщением, а затем снова перевести текст с красного на черный.

Когда я использую Statefulвиджет с RestartableTimer, и я сначала запускаю свой интерфейс, кажется, все работает нормально.Однако, как только из определенного источника приходит сообщение second , вот где что-то ломается.Если я использую обычный текст () для отображения нового сообщения, оно работает просто отлично.Но если я использую виджет Stateful для отображения текста и постепенно исчезает с красного на черный, пользовательский интерфейс остается устаревшим, и никакое количество обновлений, похоже, не помогает.

Моя иерархия (с удаленными компонентами макета) выглядитнапример:

Stateful Container for different sources
|
List View
|
+--- Stateless Custom Widget
     |
     +-- Stateless Text (displays the source)
     |
     +-- Stateful Custom Text class
<repeated for each source>

Если я заменю свой пользовательский класс обычным текстом (), то текст внезапно обновится очень хорошо (но цвет не исчезнет).

Пользовательский виджет без сохранения состояния

  Widget _CreateDataTable() {
    const Color start_color = Colors.red;
    const Color end_color = Colors.black;
    const Duration fade_len = Duration(seconds: 10);
    var read_time = DateTime.fromMillisecondsSinceEpoch(
        _data.data.timeSeconds * 1000,
        isUtc: true);
    String date_text = read_time.toLocal().toString();
    List<String> labels = ["Temperature", "Humidity", "RSSI", "Last Reading"];
    List<Widget> data = [
      Text(_data.data.temperature.toStringAsFixed(2) + " C"),
      Text(_data.data.humidity.toStringAsFixed(2) + "%"),
      Text(_data.rssi.toString() + " dB"),
      FlashText(date_text, start_color, end_color, read_time, fade_len),
      // Text(date_text),
      //
      //  IF I COMMENT OUT FlashText AND UNCOMMENT Text, EVERYTHING
      //  WORKS FINE. IF I KEEP IT AS IS, I ONLY SEE THE FIRST EVER
      //  MESSAGE AND NEW MESSAGES, WHICH I CAN CONFIRM RECEIVING VIA
      //  debugPrint, ARE SEEMINGLY IGNORED.
    ];

    var rows = List<Row>();
    for (int i = 0; i < labels.length; ++i) {
      rows.add(Row(children: [
        IntrinsicWidth(child: SizedBox(width: 10.0)),
        IntrinsicWidth(child: Text(labels[i] + ":")),
        IntrinsicWidth(child: data[i]),
        Expanded(child: SizedBox()),
      ]));
    }
    return Column(children: rows);

Пользовательский виджет с сохранением состояния

class FlashText extends StatefulWidget {
  final Color _color0;
  final Color _color1;
  final DateTime _start;
  final Duration _duration;
  final String _text;

  FlashText(
      this._text, this._color0, this._color1, this._start, this._duration);

  @override
  _FlashTextState createState() {
    debugPrint("Creating state for $_text - $_start");
    return _FlashTextState(_text, _color0, _color1, _start, _duration);
  }
}

class _FlashTextState extends State<FlashText> {
  Color _color0;
  Color _color1;
  DateTime _start;
  Duration _duration;
  String _text;

  Animation<double> _animation;
  AnimationController _controller;

  _FlashTextState(
      this._text, this._color0, this._color1, this._start, this._duration);

  @override
  Widget build(BuildContext ctx) {
    return Text(_text,
        style: TextStyle(color: Color.lerp(_color0, _color1, _GetT())));
  }

  @override
  void initState() {
    super.initState();
    RestartableTimer(_duration ~/ 30, _Update);
  }

  double _GetT() {
    DateTime now = DateTime.now().toUtc();
    return min(1, max(0, now.difference(_start).inMilliseconds /
    _duration.inMilliseconds.toDouble()));
  }

  void _Update() {
    if (_GetT() < 1.0) RestartableTimer(_duration ~/ 30, _Update);
  }
}

1 Ответ

0 голосов
/ 29 декабря 2018

Я думаю, вам нужно изменить дизайн виджета FlashText и использовать метод didUpdateWidget для перестройки при изменении текста виджета.

Я реализовал ниже полное приложение, используя текстовое поле икнопка для имитации текста, поступающего с сервера.

Я думаю, что это более или менее то, что вам нужно, но это может потребовать некоторых настроек.

import 'dart:async';

import 'package:flutter/material.dart';

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

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

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  var _cont = TextEditingController();

  String text = "qwertrrr";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Test"),
      ),
      body: Column(
        children: <Widget>[
          TextFormField(
            controller: _cont,
          ),
          RaisedButton(
            child: new Text("Test"),
            onPressed: () => setState(() {
                  text = _cont.text;
                }),
          ),
          FlashText(text, Colors.red, Colors.black, 10),
        ],
      ),
    );
  }
}

class FlashText extends StatefulWidget {
  final Color color0;
  final Color color1;
  final int duration;
  final String text;

  FlashText(this.text, this.color0, this.color1, this.duration);

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

class _FlashTextState extends State<FlashText>
    with SingleTickerProviderStateMixin<FlashText> {
  Animation<Color> _animation;
  AnimationController _controller;

  Timer timer;

  @override
  void didUpdateWidget(FlashText oldWidget) {
    if (widget.text != oldWidget.text) {
      _controller.forward(from: 0.0);
    }
    super.didUpdateWidget(oldWidget);
  }

  @override
  Widget build(BuildContext ctx) {
    return Text(
      widget.text,
      style: TextStyle(color: _animation.value),
    );
  }

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
        vsync: this, duration: Duration(seconds: widget.duration));
    _animation = ColorTween(begin: widget.color0, end: widget.color1)
        .animate(_controller)
          ..addListener(() => setState(() {}));
    _controller.forward();
  }
}
...