Пакет содержит следующие декарирующие классы: Property<T>
, Command
, ParameterizedCommand<T>
,
AsyncCommand
, ParameterizedAsyncCommand<T>
. Данные классы могут быть полезны при создании въюмоделей на основе классов расширяющих ChangeNotifier.
В классе въюмодели class FirstPageNotifier extends ChangeNotifier
определены свойство и команды.
Для хранения значения счетчика
late final outputProperty = Property<int>(
initialValue: 0,
notifyListeners: notifyListeners,
);
Задано начальное значение свойства и вызов метода notifyListeners()
в случае
изменения значения данного свойства.
В методе build(BuildContext context)
класса FirstPage extends StatelessWidget
получаем отслеживаемую ссылку на значение свойства
final output =
FirstPageInheritedNotifier.watchNotifier(context).outputProperty.value;
```,
вывод значения осуществляется следующим образом
```dart
Text(
output.toString(),
style: Theme.of(context).textTheme.headlineLarge,
),
Для инкремента и декремента счетчика созданы две команды
late final incrementCommand = Command(
action: () => outputProperty.value += 1,
canAction: () => outputProperty.value < 3,
);
late final decrementCommand = Command(
action: () => outputProperty.value -= 1,
canAction: () => outputProperty.value > 0,
);
В качестве аргументов для параметра action
передаются методы изменяющие значение свойства счетчика.
В качестве аргументов для параметра canAction
, который ограничивает доступность команды на выполнение,
передаются методы ограничивающие диапозон значений счетчика.
В классе FirstPage
в методе build(BuildContext context)
получаем ссылки на команды
final incrementCommand =
FirstPageInheritedNotifier.readNotifier(context).incrementCommand;
final decrementCommand =
FirstPageInheritedNotifier.readNotifier(context).decrementCommand;
Использование команд для работы кнопок
ElevatedButton(
onPressed: decrementCommand.canExecute()
? decrementCommand.execute
: null,
child: const Icon(Icons.exposure_minus_1),
),
и
ElevatedButton(
onPressed: incrementCommand.canExecute()
? incrementCommand.execute
: null,
child: const Icon(Icons.plus_one),
),
Можно видеть, что в методе параметра onPressed
происходит проверка возможности выполнения команды,
что влияет на доступность кнопки для клика.
В классе въюмодели SecondPageNotifier extends ChangeNotifier
определены свойство и команда.
Для отображения значения счетчика определено одно свойство, которое задано только своим начальным значением.
final outputProperty = Property<int>(initialValue: 0);
В классе SecondPage
в методе build(BuildContext context)
получаем отслеживаемую ссылку на значение свойства
final output =
SecondPageInheritedNotifier.watchNotifier(context).outputProperty.value;
Отображение значения свойства
Text(
output.toString(),
style: Theme.of(context).textTheme.headlineLarge,
),
Для кнопок опредена одна общая параметризованная команда,
которая при вызове на выполнение в качестве аргумента принимает int
величину.
late final changeCommand = ParameterizedCommand<int>(
action: (value) => outputProperty.value += value,
notifyListeners: notifyListeners,
);
При выполнении метода команды происходит вызов notifyListeners()
.
В классе SecondPage
в методе build(BuildContext context)
получаем ссылку на команду
final command =
SecondPageInheritedNotifier.readNotifier(context).changeCommand;
Использование команды для кнопок выглядит следующим образом
ElevatedButton(
onPressed: () => command(-1),
child: Text(
'-1',
style: Theme.of(context).textTheme.titleLarge,
),
),
...
ElevatedButton(
onPressed: () => command(3),
child: Text(
'3',
style: Theme.of(context).textTheme.titleLarge,
),
),
В классе въюмодели ThirdPageNotifier extends ChangeNotifier
определены три свойства и команда.
Для CheckboxListTile
задано свойство с вызовом notifyListeners()
.
late final isEnabledProperty = Property<bool>(
initialValue: false,
notifyListeners: notifyListeners,
);
```.
Далее в `_ThirdPageState` получаем отслеживаемую ссылку на это свойство
```dart
final isEnabledProperty =
ThirdPageInheritedNotifier.watchNotifier(context).isEnabledProperty;
и используем таким образом
CheckboxListTile(
title: const Text('enable the input line'),
controlAffinity: ListTileControlAffinity.platform,
contentPadding: const EdgeInsets.all(50),
value: isEnabledProperty.value,
onChanged: (value) {
isEnabledProperty.value = value!;
},
),
Для TextField
задано свойство с вызовом notifyListeners()
и правилами верификации.
late final inputProperty = Property<String>(
initialValue: '',
notifyListeners: notifyListeners,
verificationRules: <String, bool Function(String)>{
'The value cannot be an empty string': (value) => value.isEmpty,
'The length of the string cannot be less than 3 characters': (value) =>
value.length < 3,
},
);
Для правила верификации в качестве ключа указывается текстовое
сообщение для пользователя, а в качестве значения метод возвращающий true
.
В InputTextWidget
получем ссылки на свойства
final isEnabledProperty =
ThirdPageInheritedNotifier.watchNotifier(context).isEnabledProperty;
final inputProperty =
ThirdPageInheritedNotifier.readNotifier(context).inputProperty;
и используем таким образом
TextField(
decoration: InputDecoration(
border: const OutlineInputBorder(),
labelText: 'Enter the Value',
errorText: inputProperty.hasErrors ? inputProperty.errors[0] : null,
),
enabled: isEnabledProperty.value,
controller: controller,
onChanged: (text) {
inputProperty.value = text;
},
),
В этой строке errorText: inputProperty.hasErrors ? inputProperty.errors[0] : null,
определена
логика отображения сообщений пользователю об ошибке при вводе данных.
Доступность TextField
для ввода привязана к значению CheckboxListTile
через
свойство isEnabledProperty
в этой строке enabled: isEnabledProperty.value,
.
Свойство inputProperty
обновляет свое значение в методе определенном для onChanged
.
Для вывода результата используется простое свойство
final outputProperty = Property<String>(initialValue: '');
.
В примере используется одна асинхронная команда для кнопки
late final submitCommand = AsyncCommand(
action: () async {
await Future.delayed(const Duration(milliseconds: 100));
outputProperty.value = inputProperty.value;
inputProperty.value = '';
isEnabledProperty.value = false;
},
canAction: () => inputProperty.hasErrors == false,
notifyListeners: notifyListeners,
);
Доступность кнопки зависит от наличия ошибок при вводе с помощью
параметра canAction: () => inputProperty.hasErrors == false,
.
При выполнении метода команды происходит вызов notifyListeners()
.
В _ThirdPageState
получаем ссылку на команду
final submitCommand =
ThirdPageInheritedNotifier.readNotifier(context).submitCommand;
и используем команду для кнопки таким образом.
ElevatedButton(
onPressed: submitCommand.canExecute()
? (() async {
await submitCommand.execute();
controller.clear();
})
: null,
child: const Icon(Icons.done),
),
В классе въюмодели FourthPageNotifier extends ChangeNotifier
определены одно свойство
и три команды.
Для отображения списка создано свойство.
final peopleProperty = Property<List<Person>>(
initialValue: <Person>[],
);
В PeopleListViewWidget
получаем отслеживаемую ссылку на свойство.
final people =
FourthPageInheritedNotifier.watchNotifier(context).peopleProperty.value;
Отображение коллекции людей с помощью ListView.builder()
и ListTile
.
ListView.builder(
itemCount: people.length,
itemBuilder: (context, index) {
final person = people[index];
return ListTile(
onTap: () async {...},
title: Text(person.fullName),
subtitle: Text('ID: ${person.id}'),
trailing: TextButton(...),
);
},
),
Для реализации CRUD операций над коллекцией людей созданы три параметризованных асинхронных команды:
addCommand
, removeCommand
, updateCommand
.
Пример на addCommand
.
late final addCommand = ParameterizedAsyncCommand<List<String>>(
action: (value) async {
await _db.create(names: value);
peopleProperty.value = await _db.getPeople();
},
notifyListeners: notifyListeners,
);
В ComposeWidget
в методе Widget build(BuildContext context)
получаем ссылку
на команду.
final command =
FourthPageInheritedNotifier.readNotifier(context).addCommand;
Используем команду таким образом
TextButton(
onPressed: () async {
final names = <String>[
firstNameController.text,
lastNameController.text,
];
await command(names);
firstNameController.clear();
lastNameController.clear();
},
child: Text(
'Add to list',
style: Theme.of(context).textTheme.titleMedium,
))