Сегодня я хочу поделиться простым и легким материалом: ButtonStyle
и MaterialStateProperty
.
Вы помните, когда был выпущен Flutter 2.0, вместе с введением null-safety были сделаны ряд значительных изменений в API компонентов? Одним из таких изменений было отключение FlatButton
, которое требовало замены на TextButton
.
Теперь, когда мы уже используем Flutter 3.0, вы достаточно хорошо знакомы с TextButton
? А что насчет MaterialStateProperty
?
Почему TextButton
связан с MaterialStateProperty
?
Для начала стоит упомянуть Material Design, так как концепция MaterialStateProperty
основана на нём и предназначена для обеспечения совместимости взаимодействия на всех платформах.
Когда вы переходили с Flutter 1 на Flutter 2, вероятно, у вас возникал вопрос:
Почему
FlatButton
иRaisedButton
были отключены и заменены наTextButton
иElevatedButton
?
Раньше для быстрой установки цвета использовались параметры типа textColor
и backgroundColor
. Однако теперь использование ButtonStyle
кажется более сложным.
Однако официальные лица также предоставили методы, такие как styleFrom
, чтобы упростить код. Но что же является реальной причиной перехода на ButtonStyle
? И что такое MaterialStateProperty
?
Давайте рассмотрим
MaterialStateProperty
. В системе MaterialStateProperty
есть перечисление MaterialState
, которое включает следующие состояния:
TextField
Теперь понятно? С выходом платформ 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(значение);
Конечно, если вы хотите создать логику без создания нового класса, вы можете использовать статический метод `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), ), ),

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

Кроме того, иногда вам может потребоваться использовать тему для конфигурации стиля, чтобы не настраивать стиль в каждом месте отдельно.
На самом деле, `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
;Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )