Переключение между двумя виджетами в зависимости от задержки - PullRequest
0 голосов
/ 07 января 2020

У меня есть переменная состояния a. Я хочу показать виджет A, если переменная a равна 100.0. Если переменная a отличается, то я хочу показать виджет B.

Это просто и выглядит так:

      child: aOrB(a),  // somewhere in the top-level Scaffold

...

Widget aOrB(double a) {
  if (a == 100.0) {
    return A;
  } else {
    return B;
  }
}

Но вот подвох: Если переменная a изменен на 100.0, я хочу сначала показать виджет B, а после некоторой задержки переключиться на виджет A.

И еще один улов: если пользователь нажимает на виджет A, я хочу чтобы показать виджет B в течение некоторого времени, а затем через некоторое время снова отобразить виджет A.

Условия суммированы в таблицу:

|---------------------|------------------|
|      a == 100.0     |         A        |
|---------------------|------------------|
|      a != 100.0     |         B        |
|---------------------|------------------|
| a == 100.0; t < 5 s |         B        |
|  A.onTap; t < 5 s   |                  |
|---------------------|------------------|

Я уже экспериментировал с Future .delayed, Timer и виджет видимости, но не успешно.

Ответы [ 2 ]

1 голос
/ 08 января 2020

Это пример того, что я понял! Это работает для == 100, a! = 00 и задержки по нажатию. Измените его как ваше требование!

double a = 100;

  Widget focusedWidget;

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

    //if you want to predefine a initial widget call the initWidget method !
    //or call the changeWidget for instant output

    changeWidget(a);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Let\'s parse some JSON'),
        ),
        body: Container(
          padding: EdgeInsets.fromLTRB(5, 10, 5, 10),
          child: focusedWidget,
        ));
  }

  void initWidget(flag) {
    if (flag == 100) {
      setState(() {
        focusedWidget = Card(
          color: Colors.green,
          child: ListTile(
              onTap: () {
                changeWidget(50);
                //or some value
              },
              title: Text('Widget A',
                  style: TextStyle(
                      color: Colors.black, fontWeight: FontWeight.bold),
                  textAlign: TextAlign.center)),
        );
      });
    } else {
      setState(() {
        focusedWidget = Card(
          color: Colors.red,
          child: ListTile(
              onTap: () {
                changeWidget(100);
              },
              title: Text('Widget B',
                  style: TextStyle(
                      color: Colors.black, fontWeight: FontWeight.bold),
                  textAlign: TextAlign.center)),
        );
      });
    }
  }

  void changeWidget(flag) {
    if (flag == 100) {
      setState(() {
        focusedWidget = Card(
          color: Colors.red,
          child: ListTile(
            onTap: () {
              changeWidget(100);
              //or some value
            },
            title: Text('Widget B',
                style:
                    TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
                textAlign: TextAlign.center),
          ),
        );
      });
      Future.delayed(Duration(seconds: 5), () {
        setState(() {
          focusedWidget = Card(
            color: Colors.green,
            child: ListTile(
              onTap: () {
                changeWidget(50);
              },
              title: Text('Widget A',
                  style: TextStyle(
                      color: Colors.black, fontWeight: FontWeight.bold),
                  textAlign: TextAlign.center),
            ),
          );
        });
      });
    } else {
      setState(() {
        focusedWidget = Card(
          color: Colors.red,
          child: ListTile(
            onTap: () {
              changeWidget(100);
            },
            title: Text('Widget B',
                style:
                    TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
                textAlign: TextAlign.center),
          ),
        );
      });
    }
  }
0 голосов
/ 09 января 2020

Наконец-то у меня сработало следующее. Future.delayed это хорошо, но его нельзя отменить, когда Future вызывается несколько раз подряд. Таким образом, пользователь может запустить несколько 5-секундных отложенных фьючерсов за несколько секунд (либо с помощью onTap, либо путем изменения значения a, либо слишком частым запуском setState). Это делает onTap совершенно непригодным для использования, потому что запущенные Futures бьют onTaps и скрывают виджет B независимо от действий пользователя. Это делает ответ @Naveen Avidi не совсем полезным в реальности.

Так что для этого вам нужен таймер. И вам нужно запустить его asyn c в фоновом режиме. Я обнаружил, что единственное место, где можно запустить таймер в фоновом режиме, это до возврата в методе build(). Код здесь вызывается каждый раз, когда приложение перерисовывается, поэтому вы можете проверить, действительно ли a изменилось, если вы действительно хотите запустить таймер, отменить предыдущий таймер и запустить новый.

Самый простой способ прослушать изменения setState в a - продублировать значение a и сравнивать его каждый раз, когда приложение перерисовывается. Это то же самое место в коде.

double previousA;
bool showB;
Timer bTimer;

...

@override
Widget build(BuildContext context) {
  if (previousA != a) {
    if (a == 100.0) {
      showB = true;
      bTimer?.cancel();
      bTimer = Timer(Duration(seconds: 5), () {
        setState(() => showB = false);
      });
    } else {
      showB = true;
    }
    previousA = a;
  }
  return Scaffold(

...

        child: aOrB(showB),

...

aOrB(showB){
  if (showB == true) {

  } else {

  }
}

Если вы также хотите установить showB где-то в вашем коде, независимо от того, не изменился ли a, вы также можете дублировать showB и прослушать его изменения:

if (previousShowB != showB) {
  if (showB == true) {
    bTimer?.cancel();
    bTimer = Timer(Duration(seconds: 5), () {
      setState(() => showB = false);
    });
  }
}

...

setState(() => showB = true);
...