закрытие всех `открытых диалогов` и` всплывающих меню` перед удалением текущего виджета - PullRequest
0 голосов
/ 06 марта 2020

Я работал над проектом, который требует, чтобы я изменил виджет на экране на основе orientation.

мобильного устройства. Например, в случае ориентации portrait мне нужно показать виджет (скажем, портретный виджет) и в случае ориентации landscape мне нужно показать другой виджет (назовем его ландшафтным виджетом).

Я использовал OrientationBuilder

Актуальная проблема: При изменении ориентации все Dialogs и OptionMenu или любой другой виджет всплывающего вида не закрывается. Как мне закрыть их при смене ориентации?

Действия по воспроизведению проблемы:

  1. Запустите приведенный ниже код приложения [пример приложения для воспроизведения проблемы]
  2. Сделайте долгое нажатие на теле приложения, чтобы увидеть диалоговое окно или щелкните OptionMenu в верхнем левом углу
  3. Измените ориентацию устройства, вы увидите, что тело виджета изменилось в соответствии с ориентацией, но виджет Popuped все еще виден.

пример кода приложения:

// main.dart
import 'package:flutter/material.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(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return OrientationBuilder(builder: (context, orientation) {
      bool isLandscape = orientation == Orientation.landscape;
      return isLandscape ? Landscape() : Portrait();
    });
  }
}

class Portrait extends StatefulWidget {
  @override
  _PortraitState createState() => _PortraitState();
}

class _PortraitState extends State<Portrait> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: buildTitle(),
        actions: <Widget>[_buildOptionMenu(context)],
      ),
      body: GestureDetector(
        onLongPress: () {
          showDialog(
            context: context,
            builder: (context) => AlertDialog(
              content: buildTitle(),
            ),
          );
        },
        child: Container(
          color: Colors.blue.withOpacity(0.4),
          child: Stack(
            fit: StackFit.expand,
            children: <Widget>[
              Center(
                child: buildTitle(),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildOptionMenu(BuildContext context) {
    return PopupMenuButton(itemBuilder: (context) {
      var list = <String>['Portrait-Item-1', 'Portrait-Item-2'];
      return list
          .map<PopupMenuEntry<String>>(
            (e) => PopupMenuItem<String>(
              child: Text(e),
            ),
          )
          .toList();
    });
  }

  Text buildTitle() => Text('Portrait');
}

class Landscape extends StatefulWidget {
  @override
  _LandscapeState createState() => _LandscapeState();
}

class _LandscapeState extends State<Landscape> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: buildTitle(),
        actions: <Widget>[_buildOptionMenu(context)],
      ),
      body: GestureDetector(
        onLongPress: () {
          showDialog(
            context: context,
            builder: (context) => AlertDialog(
              content: buildTitle(),
            ),
          );
        },
        child: Container(
          color: Colors.orange.withOpacity(0.3),
          child: Stack(
            fit: StackFit.expand,
            children: <Widget>[
              Center(
                child: buildTitle(),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Text buildTitle() => Text('Landscape');

  Widget _buildOptionMenu(BuildContext context) {
    return PopupMenuButton(itemBuilder: (context) {
      var list = <String>[
        'Landscape-Item-1',
        'Landscape-Item-2',
        'Landscape-Item-3',
      ];
      return list
          .map<PopupMenuEntry<String>>(
            (e) => PopupMenuItem<String>(
              child: Text(e),
            ),
          )
          .toList();
    });
  }
}

Я не смог найти подходящее решение. Есть некоторые решения, которые требуют прислушиваться к изменениям ориентации и push / pop виджетам, основанным на новой ориентации.

Но работать с ним слишком сложно, и для него требуется добавить код одного и того же типа как в книжной, так и в альбомной ориентации. Что также не масштабируется в случае, если мне нужно обрабатывать дополнительные ориентации, такие как, обратный портрет и обратный ландшафт.

Обновление

Одно из возможных решений - это вытолкнуть все виджеты до виджета root, а затем pu sh новый виджет на основе ориентации. Это работает, но имеет побочный эффект.

Например, если я наберу sh какой-нибудь новый виджет из портрета (скажем, страницу входа).

Затем, если я поверну свое устройство в альбомную ориентацию, оно должно наполнить интерфейс страницы входа в систему в альбомной ориентации, но в соответствии с кодом, который отображает весь виджет до root.

То, что я увижу, это виджет Пейзаж вместо страницы входа в альбомном режиме.

Для ясности :

Я ищу ответ закрыть все открытые диалоги / всплывающие окна до того, как родительский виджет станет disposed. Решение не должно зависеть от извлечения всего виджета из навигатора.

Что-то, что я узнал из представления дерева виджетов, состоит в том, что виджеты, которые отображаются как всплывающее окно (всплывающее окно) меню или диалоговое окно) находятся в другой ветке виджета непосредственно из MaterialApp.

Проверьте эти снимки экрана:

Видимое всплывающее меню в виджете Пейзаж:

Pop up menu in Landscape widget

Видимый диалог в виджете Пейзаж:

Dialog in Landscape widget

Видимое всплывающее меню в виджете Портрет:

Pop up menu in Portrait widget

Видимый диалог в виджете Портрет:

Dialog in Portrait widget

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

1 Ответ

2 голосов
/ 20 апреля 2020

Согласно приведенному коду, я изменил детектор жестов в коде диалога длинного нажатия и его дочернем коде, как показано ниже:

// alert dialog code
...
builder: (context) => AlertDialog(
              content:
                  MediaQuery.of(context).orientation == Orientation.portrait
                      ? Text('Portrait')
                      : Text('Landscape'),
            ),
...

// gesture detector  child code

...
Center(
    child: MediaQuery.of(context).orientation == Orientation.portrait
                        ? Text('Portrait')
                        : Text('Landscape'),
),
...

Вывод:
1

Вывод: MediaQuery.of (контекст) .orientation обрабатывает сам себя.

Обновление:
Если вы go с жизненным циклом при изменении ориентации с помощью этого кода будет вызываться только метод сборки, а не метод утилизации. Вы можете удалить все всплывающие окна при вызове метода сборки.
Проверьте приведенный ниже код.
Здесь при длительном нажатии я открыл 3 всплывающих окна для демонстрационной цели (может быть любое количество всплывающих окон или меню) ... Во время изменения ориентации, Navigator.of(context).popUntil((route) => route.isFirst); будет вызван первым и сначала выскочит все всплывающие окна и меню.

// main.dart
import 'package:flutter/material.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(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return OrientationBuilder(builder: (context, orientation) {
      bool isLandscape = orientation == Orientation.landscape;
      return isLandscape ? Landscape() : Portrait();
    });
  }
}

class Portrait extends StatefulWidget {
  @override
  _PortraitState createState() => _PortraitState();
}

class _PortraitState extends State<Portrait> {
  // this method will not be called when orientation changes
//  @override
//  void dispose() {
//    super.dispose();
//    Navigator.pop(context);
//    print("Portrait dispose");
//  }

  @override
  Widget build(BuildContext context) {
    // the below line will pop all the popups
    Navigator.of(context).popUntil((route) => route.isFirst);
    // code to check this method is called when orientation is changed
    print("Portrait build");
    return Scaffold(
      appBar: AppBar(
        title: buildTitle(),
        actions: <Widget>[_buildOptionMenu(context)],
      ),
      body: GestureDetector(
        onLongPress: () {
          showDialog(
            context: context,
            builder: (context) => AlertDialog(
              content: Center(
                child:
                    MediaQuery.of(context).orientation == Orientation.portrait
                        ? Text('Portrait')
                        : Text('Landscape'),
              ),
            ),
          );
          showDialog(
            context: context,
            builder: (context) => AlertDialog(
              content: Center(
                child:
                    MediaQuery.of(context).orientation == Orientation.portrait
                        ? Text('Portrait')
                        : Text('Landscape'),
              ),
            ),
          );
          showDialog(
            context: context,
            builder: (context) => AlertDialog(
              content: Center(
                child:
                    MediaQuery.of(context).orientation == Orientation.portrait
                        ? Text('Portrait')
                        : Text('Landscape'),
              ),
            ),
          );
        },
        child: Container(
          color: Colors.blue.withOpacity(0.4),
          child: Stack(
            fit: StackFit.expand,
            children: <Widget>[
              Center(
                child: Center(
                  child:
                      MediaQuery.of(context).orientation == Orientation.portrait
                          ? Text('Portrait')
                          : Text('Landscape'),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildOptionMenu(BuildContext context) {
    return PopupMenuButton(itemBuilder: (context) {
      var list = <String>['Portrait-Item-1', 'Portrait-Item-2'];
      return list
          .map<PopupMenuEntry<String>>(
            (e) => PopupMenuItem<String>(
              child: Text(e),
            ),
          )
          .toList();
    });
  }

  Text buildTitle() => Text('Portrait');
}

class Landscape extends StatefulWidget {
  @override
  _LandscapeState createState() => _LandscapeState();
}

class _LandscapeState extends State<Landscape> {
  // this method will not be called when orientation changes
//  @override
//  void dispose() {
//    super.dispose();
//    Navigator.pop(context);
//    print("Landscape dispose");
//  }

  @override
  Widget build(BuildContext context) {
    // the below line will pop all the popups
    Navigator.of(context).popUntil((route) => route.isFirst);
    // code to check this method is called when orientation is changed
    print("Landscape build");
    return Scaffold(
      appBar: AppBar(
        title: buildTitle(),
        actions: <Widget>[_buildOptionMenu(context)],
      ),
      body: GestureDetector(
        onLongPress: () {
          showDialog(
            context: context,
            builder: (context) => AlertDialog(
              content: Center(
                child:
                    MediaQuery.of(context).orientation == Orientation.portrait
                        ? Text('Portrait')
                        : Text('Landscape'),
              ),
            ),
          );
          showDialog(
            context: context,
            builder: (context) => AlertDialog(
              content: Center(
                child:
                    MediaQuery.of(context).orientation == Orientation.portrait
                        ? Text('Portrait')
                        : Text('Landscape'),
              ),
            ),
          );
          showDialog(
            context: context,
            builder: (context) => AlertDialog(
              content: Center(
                child:
                    MediaQuery.of(context).orientation == Orientation.portrait
                        ? Text('Portrait')
                        : Text('Landscape'),
              ),
            ),
          );
        },
        child: Container(
          color: Colors.orange.withOpacity(0.3),
          child: Stack(
            fit: StackFit.expand,
            children: <Widget>[
              Center(
                child: Center(
                  child:
                      MediaQuery.of(context).orientation == Orientation.portrait
                          ? Text('Portrait')
                          : Text('Landscape'),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Text buildTitle() => Text('Landscape');

  Widget _buildOptionMenu(BuildContext context) {
    return PopupMenuButton(itemBuilder: (context) {
      var list = <String>[
        'Landscape-Item-1',
        'Landscape-Item-2',
        'Landscape-Item-3',
      ];
      return list
          .map<PopupMenuEntry<String>>(
            (e) => PopupMenuItem<String>(
              child: Text(e),
            ),
          )
          .toList();
    });
  }
}

Выход:
enter image description here

...