1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/CarGuo-GSYFlutterBook

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Flutter-N1.md 11 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 10.03.2025 00:06 5767d61

Небольшие хитрости в Flutter: ButtonStyle и MaterialStateProperty

Сегодня я хочу поделиться простым и легким материалом: ButtonStyle и MaterialStateProperty.

Вы помните, когда был выпущен Flutter 2.0, вместе с введением null-safety были сделаны ряд значительных изменений в API компонентов? Одним из таких изменений было отключение FlatButton, которое требовало замены на TextButton.

Теперь, когда мы уже используем Flutter 3.0, вы достаточно хорошо знакомы с TextButton? А что насчет MaterialStateProperty?

Почему TextButton связан с MaterialStateProperty?

Для начала стоит упомянуть Material Design, так как концепция MaterialStateProperty основана на нём и предназначена для обеспечения совместимости взаимодействия на всех платформах.

image-20220530103804444

Когда вы переходили с Flutter 1 на Flutter 2, вероятно, у вас возникал вопрос:

Почему FlatButton и RaisedButton были отключены и заменены на TextButton и ElevatedButton?

image-20220530104346216

Раньше для быстрой установки цвета использовались параметры типа textColor и backgroundColor. Однако теперь использование ButtonStyle кажется более сложным.

Однако официальные лица также предоставили методы, такие как styleFrom, чтобы упростить код. Но что же является реальной причиной перехода на ButtonStyle? И что такое MaterialStateProperty?

image-20220530104739603Давайте рассмотрим MaterialStateProperty. В системе MaterialStateProperty есть перечисление MaterialState, которое включает следующие состояния:

  • disabled: Когда компонент или элемент недоступен для взаимодействия
  • hovered: При наведении курсора мыши
  • focused: При фокусировании через клавиатуру
  • selected: Например, состояние выбора чекбокса
  • pressed: При нажатии кнопкой, клавиатурой или пальцем
  • dragged: При долгом нажатии и перемещении компонента
  • error: При ошибке, например, состоянии ошибки TextField

image-20220530114532550Теперь понятно? С выходом платформ Web и Desktop прежний FlatButton уже не мог удовлетворять потребностям новых UI взаимодействий, таких как состояние наведения мыши и клавиш. Поэтому TextButton начал использовать MaterialStateProperty для составления ButtonStyle, чтобы поддерживать отображение состояния UI на разных платформах.Прежде чем использовать многоплатформенные адаптации, вы могли бы сделать что-то вроде этого, чтобы обрабатывать множество различных состояний:

  getStateColor(Set<MaterialState> states) {
    if (states.contains(MaterialState.hovered)) {
      /// Когда состояние наведения совпадает с сосредоточенным
      if (states.contains(MaterialState.focused)) {
        return Colors.red;
      } else {
        return Colors.blue;
      }
    } else if (states.contains(MaterialState.focused)) {
      return Colors.yellow;
    }
    return Colors.green;
  }
```Но теперь вам достаточно просто расширить `MaterialStateProperty`, а затем переопределить метод `resolve`. Например, эффект наведения (`наведённый`) в компоненте `TextButton` реализуется через внутренний класс `_TextButtonDefaultOverlay`.```dart
@immutable
class _TextButtonDefaultOverlay extends MaterialStateProperty<Color?> {
  _TextButtonDefaultOverlay(this.primary);

  final Color primary;

  @override
  Color? resolve(Set<MaterialState> states) {
    if (states.contains(MaterialState.hovered))
      return primary.withOpacity(0.04);
    if (states.contains(MaterialState.focused) || states.contains(MaterialState.pressed))
      return primary.withOpacity(0.12);
    return null;
  }

  @override
  String toString() {
    return '{hovered: ${primary.withOpacity(0.04)}, focused,pressed: ${primary.withOpacity(0.12)}, otherwise: null}';
  }
}

изображение

На самом деле внутри TextButton используется метод styleFrom для конфигурирования необходимых эффектов MaterialState. Внутри него используются следующие классы:

  • _TextButtonDefaultForeground: Обрабатывает отключённое состояние, меняя цвет на onSurface?.withOpacity(0.38)
  • _TextButtonDefaultOverlay: Обрабатывает эффекты наведения, сосредоточения и нажатия, используя primary.withOpacity
  • _TextButtonDefaultMouseCursor: Обрабатывает отключение курсора мыши.

Оставшиеся параметры добавляются с помощью хорошо известной функции ButtonStyleButton.allOrNull, которая представляет собой версию с возможностью null для метода MaterialStateProperty.all.

А что делает ButtonStyleButton.allOrNull?

Фактически ButtonStyleButton.allOrNull является версией с возможностью null для метода MaterialStateProperty.all. Таким образом, если требуется поддержка null, можно использовать MaterialStateProperty.all.```dart static MaterialStateProperty? всеИлиНоль(T? значение) => значение == null ? null : MaterialStateProperty.all(значение);


![изображение-20220530142530429](http://img.cdn.guoshuyu.cn/20220531_N/image6.png)Конечно, если вы хотите создать логику без создания нового класса, вы можете использовать статический метод `resolveWith`, как показано ниже:

TextButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith((states) { if (states.contains(MaterialState.hovered)) { return Colors.green; } return Colors.transparent; })), onPressed: () {}, child: new Text( "ТЕСТ", style: TextStyle(fontSize: 100), ), ),


![](http://img.cdn.guoshuyu.cn/20220531_N/image7.gif)

Конечно, Google при реализации UI-ответов для компонентов Flutter в соответствии с Material Design также следует этим правилам, например, при наведении курсора `primary.withOpacity(0.04);`. Поэтому, вне зависимости от того, используется ли `TextButton` или `RaisedButton`, они следуют аналогичным правилам.

![изображение-20220530113735250](http://img.cdn.guoshuyu.cn/20220531_N/image8.png)

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

На самом деле, `TextButton`, `ElevatedButton` и `OutlinedButton` являются подклассами `ButtonStyleButton`, и они следуют таким принципам:

```dart
final ButtonStyle? виджетСтайл = widget.style;
final ButtonStyle? темаСтайл = widget.themeStyleOf(context);
final ButtonStyle дефолтныйСтиль = widget.defaultStyleOf(context);
assert(дефолтныйСтиль != null);
```    T? эффективноеОборачивание<T>(T? Function(ButtonStyle? style) получитьСвойство) {
      final T? виджетЗначение  = получитьСвойство(виджетСтайл);
      final T? темаЗначение   = получитьСвойство(темаСтайл);
      final T? дефолтноеЗначение = получитьСвойство(дефолтныйСтиль);
      return виджетЗначение ?? темаЗначение ?? дефолтноеЗначение;
    }
```то есть `return виджетЗначение ?? темаЗначение ?? дефолтноеЗначение;`, где:

- `виджетЗначение` — это отдельно настроенный стиль компонента;
- `темаЗначение` — это глобальный стиль, настроенный в теме;
- `дефолтноеЗначение` — это встроенный дефолтный стиль, то есть статический метод `styleFrom`, который также использует некоторые объекты `ThemeData`, такие как `colorScheme.primary`, `textTheme.button`, `shadowColor` и так далее;

Пример того, как глобально удалить эффект водяного знака при нажатии кнопки `TextButton`, показан ниже:

```dart
theme: ThemeData(
  primarySwatch: Colors.blue,
  textButtonTheme: TextButtonThemeData(
    // Удаление эффекта водяного знака для TextButton
    style: ButtonStyle(splashFactory: NoSplash.splashFactory),
  ),
),

Наконец, можно сделать вывод:

  • Для простого изменения цвета фона используйте styleFrom;
  • Для отдельной конфигурации используйте ButtonStyleButton.allOrNull;
  • Для гибкого управления используйте ButtonStyleButton.resolveWith или реализуйте метод resolve интерфейса MaterialStateProperty;

image-20220530151634041

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/CarGuo-GSYFlutterBook.git
git@api.gitlife.ru:oschina-mirror/CarGuo-GSYFlutterBook.git
oschina-mirror
CarGuo-GSYFlutterBook
CarGuo-GSYFlutterBook
master