Я учусь использовать пакет async-redux
во Флаттере.
https://pub.dev/packages/async_redux
Образцы хороши по большей части, но довольно просты c. В частности, они показывают, как вызвать действие со страницы (т. Е. Как подключить кнопку к действию через событие onPressed
). Это прекрасно работает. Однако они не показывают, как передать значение со страницы в действие. Поэтому я могу запустить событие save
, но не могу отправить ему обновленные значения со страницы. Я свел свой код к следующему коду.
Кто-нибудь знает, как читать значения из textControllers и передавать их в действие? Я новичок во Flutter и пакете async-redux
, и мне трудно разобраться, как он работает.
Для справки приведенный мною код должен считывать имена и фамилии из формы и отправлять их по адресу Firebase для хранилища (очевидно, что реальное приложение будет более задействованным, чем это).
Кроме того, я подтвердил, что действие достигается, потому что выполняется оператор debugPrint()
в SaveSettingsAction
.
import 'package:async_redux/async_redux.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/gestures.dart' show DragStartBehavior;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
////////////////////////////////////////////////////////////////////////////////
// Main method and app.
////////////////////////////////////////////////////////////////////////////////
Store<AppState> store;
void main() {
var state = AppState.initialState();
store = Store<AppState>(initialState: state);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StoreProvider<AppState>(
store: store,
child: MaterialApp(
home: SettingsPage(),
title: 'Sample App',
)
);
}
}
////////////////////////////////////////////////////////////////////////////////
// App state.
////////////////////////////////////////////////////////////////////////////////
class AppState {
final String firstName;
final String lastName;
AppState({
this.firstName,
this.lastName,
});
AppState copy({String firstName, String lastName}) {
return new AppState(
firstName: firstName ?? this.firstName,
lastName: lastName ?? this.lastName,
);
}
static AppState initialState() => AppState(
firstName: "",
lastName: "",
);
@override
int get hashCode =>
firstName.hashCode ^
lastName.hashCode;
bool operator ==(Object other) =>
identical(this, other) ||
other is AppState &&
runtimeType == other.runtimeType &&
firstName == other.firstName &&
lastName == other.lastName;
@override
String toString() {
return 'AppState{'
'firstName: $firstName,'
'lastName: $lastName}';
}
}
////////////////////////////////////////////////////////////////////////////////
// Save settings action (ignore the persistence logic here - this is just a
// placeholder until we can read from the page properly).
////////////////////////////////////////////////////////////////////////////////
class SaveSettingsAction extends ReduxAction<AppState> {
final String firstName;
final String lastName;
SaveSettingsAction({this.firstName, this.lastName});
@override
Future<AppState> reduce() async {
// Log that this action has been reached.
debugPrint("Start of reduce function in SaveSettingsAction.");
// Add values to Firebase (so we have history).
Firestore.instance
.collection('users')
.add({
"firstName": firstName,
"lastName": lastName,
});
// Return state with updated values.
return state.copy(
firstName: this.firstName,
lastName: this.lastName,
);
}
}
////////////////////////////////////////////////////////////////////////////////
// Settings connector.
////////////////////////////////////////////////////////////////////////////////
class SettingsConnector extends StatelessWidget {
SettingsConnector({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, SettingsViewModel>(
model: SettingsViewModel(),
builder: (BuildContext context, SettingsViewModel vm) => SettingsPage(
firstName: vm.firstName,
lastName: vm.lastName,
onSaveSettings: vm.onSaveSettings,
),
);
}
}
////////////////////////////////////////////////////////////////////////////////
// Settings view model.
////////////////////////////////////////////////////////////////////////////////
class SettingsViewModel extends BaseModel<AppState> {
SettingsViewModel();
String firstName;
String lastName;
VoidCallback onSaveSettings;
SettingsViewModel.build({
@required this.firstName,
@required this.lastName,
@required this.onSaveSettings,
}) : super (equals: [firstName, lastName]);
@override
SettingsViewModel fromStore() => SettingsViewModel.build(
firstName: state.firstName,
lastName: state.lastName,
onSaveSettings: () => dispatch(SaveSettingsAction()),
);
}
////////////////////////////////////////////////////////////////////////////////
// Settings page.
////////////////////////////////////////////////////////////////////////////////
class SettingsPage extends StatelessWidget {
final String firstName;
final String lastName;
final VoidCallback onSaveSettings;
final TextEditingController firstNameController;
final TextEditingController lastNameController;
SettingsPage({
Key key,
this.firstName,
this.lastName,
this.onSaveSettings,
this.firstNameController,
this.lastNameController,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Scrollbar(
child: SingleChildScrollView(
dragStartBehavior: DragStartBehavior.down,
padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
TextFormField(
controller: firstNameController,
decoration: const InputDecoration(
icon: Icon(Icons.person),
labelText: 'First Name',
),
initialValue: firstName,
),
TextFormField(
controller: lastNameController,
decoration: const InputDecoration(
icon: Icon(Icons.person),
labelText: 'Last Name',
),
initialValue: lastName,
),
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: RaisedButton(
// How do we get the textController values to the onSaveSettings action?
onPressed: onSaveSettings,
child: const Text(
'Save Settings',
style: TextStyle(fontSize: 16)
),
),
),
],
),
),
),
),
],
),
);
}
}