Альтернатива закусочной Flutter или более простой способ, чем завернуть все в Scaffold?

Я работаю над своим первым приложением Flutter (отладка на моем телефоне Android). У меня есть список с элементами строки. Когда вы долго нажимаете строку, она копирует содержимое в буфер обмена пользователя. Это отлично работает!

Но мне нужно, чтобы пользователь знал, что контент был скопирован.

Я пытался следовать многим руководствам по попытке получить строку, окруженную методом сборки или внутри Scaffold, но я не могу заставить ее работать. Есть ли альтернативный способ уведомления пользователя (просто) о том, что что-то вроде «Скопировано!» произошло?

Обратите внимание на закомментированный Scaffold.of(... ниже. Просто кажется, что должен быть более простой способ уведомления пользователя, кроме обертывания всего в Scaffold. (и когда я пытаюсь, это ломает мой макет).

import 'package:flutter/material.dart';
import 'package:my_app/Theme.dart' as MyTheme;
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/services.dart';

class RowRule extends StatelessWidget {
  final DocumentSnapshot ruleGroup;

  RowRule(this.ruleGroup);

  _buildChildren() {
    var builder = <Widget>[];
    if (!ruleGroup['label'].isEmpty) {
      builder.add(new Text(ruleGroup['label'],
          style: MyTheme.TextStyles.articleContentLabelTextStyle));
    }
    if (!ruleGroup['details'].isEmpty) {
      builder.add(new Text(ruleGroup['details'],
          style: MyTheme.TextStyles.articleContentTextStyle));
    }
    return builder;
  }


  @override
  Widget build(BuildContext context) {
    return new GestureDetector(
        onLongPress: () {
          Clipboard.setData(new ClipboardData(text: ruleGroup['label'] + " "  + ruleGroup['details']));
//          Scaffold.of(context).showSnackBar(SnackBar
//            (content: Text('text copied')));
        },
        child: Container(
          margin: const EdgeInsets.symmetric(vertical: 3.0),
          child: new FlatButton(
            color: Colors.white,
            padding: EdgeInsets.symmetric(horizontal: 0.0),
            child: new Stack(
              children: <Widget>[
                new Container(
                  margin: const EdgeInsets.symmetric(
                    vertical: MyTheme.Dimens.ruleGroupListRowMarginVertical),
                  child: new Container(
                      child: Padding(
                        padding: EdgeInsets.symmetric(horizontal: 32.0, vertical: 8.0),
                        child: new Column(
                          crossAxisAlignment: CrossAxisAlignment.stretch,
                          children: _buildChildren(),
                        ),
                    )),
              )
            ],
          ),
        ),
      ));
    }
  }

Цель состоит в том, чтобы иметь такую ​​​​страницу (см. Изображение), которая у меня есть, и она работает и прокручивается... и т. д., но я не могу заставить ее работать с эшафотом и, следовательно, не смог использовать Снэк-бар. Каждая «строка» (для которой предназначен этот файл) должна отображать закусочную на longPress.

Альтернатива закусочной Flutter или более простой способ, чем завернуть все в Scaffold?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
0
6 154
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Вы можете использовать GlobalKey, чтобы заставить его работать так, как вы хотите.

Поскольку у меня нет доступа к вашей базе данных, вот как я дал вам идею сделать это. Скопируйте и вставьте этот код в свой класс и внесите соответствующие изменения. Я также считаю, что в вашем классе RowRule что-то не так, вы можете просто скопировать полный код, который я вам дал, и запустить?

void main() => runApp(MaterialApp(home: HomePage()));

class HomePage extends StatelessWidget {
  final GlobalKey<ScaffoldState> _key = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Color(0xFFFFFFFF).withOpacity(0.9),
      key: _key,
      body: Column(
        children: <Widget>[
          Container(
            color: Color.fromRGBO(52, 56, 245, 1),
            height: 150,
            alignment: Alignment.center,
            child: Container(width: 56, padding: EdgeInsets.only(top: 12), decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.yellow)),
          ),
          Expanded(
            child: ListView.builder(
              padding: EdgeInsets.zero,
              itemCount: 120,
              itemBuilder: (context, index) {
                return Container(
                  color: Colors.white,
                  margin: const EdgeInsets.all(4),
                  child: ListTile(
                    title: Text("Row #$index"),
                    onLongPress: () => _key.currentState
                      ..removeCurrentSnackBar()
                      ..showSnackBar(SnackBar(content: Text("Copied \"Row #$index\""))),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

У меня нет Scaffold в этом файле/макете. Я видел это как рекомендуемый метод, но как мне получить доступ к GlobalKey здесь? Все примеры, которые я видел, похожи на этот, где Scaffold находится в том же файле, что и longPress.

Dave 07.04.2019 14:46

@Dave Вы можете легко обернуть свой виджет в Scaffold, сделайте GestureDetector дочерним элементом Scaffold в своем примере.

CopsOnRoad 07.04.2019 17:16

Когда я оборачиваю GestureDetector в Scaffold и делаю GestureDetector «телом», я получаю: Another exception was thrown: RenderCustomMultiChildLayoutBox object was given an infinite size during layout. Та же ошибка также для «_RenderInkFeatures», «RenderPhysicalModel», «RenderRepaintBoundary» и «RenderIndexedSemantics».

Dave 07.04.2019 23:56

Есть несколько вещей, которые вам нужно сделать, например, использовать onPressed свойство FlatButton, чтобы разрешить клики, оберните GestureDetector в Scaffold. Я дополнительно изменил код, чтобы он использовал GlobalKey, чтобы упростить вам задачу.

Вот окончательный код (Твой путь)

class RowRule extends StatelessWidget {
  final GlobalKey<ScaffoldState> globalKey = GlobalKey();
  final DocumentSnapshot ruleGroup;

  RowRule(this.ruleGroup);

  _buildChildren() {
    var builder = <Widget>[];
    if (!ruleGroup['label'].isEmpty) {
      builder.add(new Text(ruleGroup['label'], style: MyTheme.TextStyles.articleContentLabelTextStyle));
    }
    if (!ruleGroup['details'].isEmpty) {
      builder.add(new Text(ruleGroup['details'], style: MyTheme.TextStyles.articleContentTextStyle));
    }
    return builder;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: globalKey,
      body: GestureDetector(
        onLongPress: () {
          Clipboard.setData(new ClipboardData(text: ruleGroup['label'] + " " + ruleGroup['details']));
          globalKey.currentState
            ..removeCurrentSnackBar()
            ..showSnackBar(SnackBar(content: Text('text copied')));
        },
        child: Container(
          margin: const EdgeInsets.symmetric(vertical: 3.0),
          child: new FlatButton(
            onPressed: () => print("Handle button press here"),
            color: Colors.white,
            padding: EdgeInsets.symmetric(horizontal: 0.0),
            child: new Stack(
              children: <Widget>[
                new Container(
                  margin: const EdgeInsets.symmetric(vertical: MyTheme.Dimens.ruleGroupListRowMarginVertical),
                  child: new Container(
                    child: Padding(
                      padding: EdgeInsets.symmetric(horizontal: 32.0, vertical: 8.0),
                      child: new Column(
                        crossAxisAlignment: CrossAxisAlignment.stretch,
                        children: _buildChildren(),
                      ),
                    ),
                  ),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Это вызывает следующую ошибку: RenderCustomMultiChildLayoutBox object was given an infinite size during layout.

Dave 12.04.2019 04:33

Я не уверен, завершен ли ваш метод build() или вы еще не изменили его, потому что он состоит из множества виджетов, которые просто избыточны. Например, нет необходимости иметь Container в Container и далее Padding вместе с FlatButton, что сделало бы кликабельным весь экран. Также наличие Column не будет хорошей идеей, потому что ваш экран может переполниться, если у вас будет больше данных. Вместо этого используйте ListView.

Итак, если вы последуете моему совету, используйте этот простой код, который должен дать вам то, что вы действительно ищете. (Смотрите, что метод build() состоит всего из 5 строк.

class RowRule extends StatelessWidget {
  final GlobalKey<ScaffoldState> globalKey = GlobalKey();

  final DocumentSnapshot ruleGroup;

  RowRule(this.ruleGroup);

  _buildChildren() {
    var builder = <Widget>[];
    if (!ruleGroup['label'].isEmpty) {
      builder.add(
        ListTile(
          title: Text(ruleGroup['label'], style: MyTheme.TextStyles.articleContentLabelTextStyle),
          onLongPress: () {
            globalKey.currentState
              ..removeCurrentSnackBar()
              ..showSnackBar(SnackBar(content: Text("Clicked")));
          },
        ),
      );
    }
    if (!ruleGroup['details'].isEmpty) {
      builder.add(
        ListTile(
          title: Text(ruleGroup['details'], style: MyTheme.TextStyles.articleContentTextStyle),
          onLongPress: () {
            globalKey.currentState
              ..removeCurrentSnackBar()
              ..showSnackBar(SnackBar(content: Text("Clicked")));
          },
        ),
      );
    }
    return builder;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: globalKey,
      body: ListView(children: _buildChildren()),
    );
  }
}

Я пробовал это и получаю много ошибок, некоторые из которых такие же, как и при попытке использовать Scaffold: «Было выбрано другое исключение: объекту _RenderInkFeatures был присвоен бесконечный размер во время макета». и ряд других.

Dave 12.04.2019 04:41

Не могли бы вы поделиться классом, где вы используете RowRule?

CopsOnRoad 12.04.2019 08:23

Я прочитал ваши комментарии ко всем ответам и вот мой вывод:

Вам нужен ScaffoldState объект, который находится чуть выше виджета в дереве, чтобы показать Snackbar. Вы можете пройти через GlobalKey, как многие предлагали. Довольно просто, если Scaffold создается внутри build виджета, но если он находится вне виджета (в вашем случае), то это усложняется. Вам нужно передать этот ключ везде, где он вам нужен, через аргументы конструктора дочерних виджетов.

Scaffold.of(context) — очень удобный способ сделать это. Как и Унаследованный виджет, Scaffold.of(BuildContext context) дает вам доступ к ближайшему ScaffoldState объекту над деревом. В противном случае получить этот экземпляр (передав его в качестве аргументов конструктора) может быть кошмаром, если ваше дерево было очень глубоким.

Извините, чтобы разочаровать, но я не думаю, что есть лучший или более чистый метод, чем этот, если вы хотите получить ScaffoldState, который не встроен в сборку этого виджета. Вы можете вызвать его в любом виджете, который имеет Scaffold в качестве родителя.

Scaffold.of(context) не имеет ничего общего с унаследованными виджетами
Rémi Rousselet 13.04.2019 15:18

@RémiRousselet Я немного поискал, и вы правы, Scafoold не является IW, но он предоставляет аналогичный .of() статический метод для получения ближайшего объекта State. Я просто предположил, что это будет IW. В любом случае исправил мой ответ. Спасибо, что просветили меня!!

Harsh Bhikadia 16.04.2019 04:03
Ответ принят как подходящий

Это простая замена плагина для Snackbar под названием «Flushbar». Вы можете получить плагин здесь - https://pub.dartlang.org/packages/flushbar Вам не нужно заботиться о какой-либо упаковке виджетов в каркас, также вы получаете множество модификаций для вас, таких как градиент фона, добавление форм и т. д. В Snackbar и все такое. Внутри вашего onLongPressed в GestureDetectore вы можете сделать это.

onLongPressed:(){
Clipboard.setData(new ClipboardData(text: ruleGroup['label'] + " "  + ruleGroup['details']));
Flushbar(
             message:  "Copied !!",
             duration:  Duration(seconds: 3),              
          )..show(context);
}

Это отобразит закусочную в вашем приложении, где вы хотели бы ее видеть, и вы можете получить множество доступных вам модификаций, чтобы вы могли сделать ее похожей на ваше приложение.

Для чего нужен буфер обмена? Это вообще связано с вашим ответом?

Jhourlad Estrella 20.07.2020 13:10

FYI: Flushbar СНЯТ С ПРОИЗВОДСТВА. pub.dartlang.org/packages/flushbar

Niraj 16.02.2021 12:02

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

Другие вопросы по теме