У меня есть виджет формы, виджет списка и виджет «обертка» или, другими словами, виджет родителя / контейнера.Таким образом, чтобы дать представление о дереве виджетов, оно таково.
- Родитель / Виджет контейнера
- Виджет формы
- Виджет кнопки
- Виджет списка
Обратите внимание, что форма, кнопки и виджет списка - все одноуровневые элементы внутри виджета родитель / контейнер.То, что я хочу, это нажать на элемент списка в виджете списка и заполнить виджет формы данными, которые передаются из виджета списка.
Вот мой родительский виджет.
import 'package:andplus_flutter_7_gui/model/user.dart';
import 'package:andplus_flutter_7_gui/services/user_service.dart';
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
import 'crud_form.dart';
import 'crud_list.dart';
class Crud extends StatefulWidget {
Crud({Key key, this.title}) : super(key: key);
final String title;
_CrudContainerState createState() => _CrudContainerState();
}
class _CrudContainerState extends State<Crud> {
List<User> users;
User user = User();
UserService userService;
@override
void initState() {
super.initState();
if (userService == null) {
userService = UserService(user);
}
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
userService.dispose();
}
@override
Widget build(BuildContext context) {
return Material(
child: Scaffold(
resizeToAvoidBottomPadding: false,
appBar: AppBar(
title: Text(widget.title),
),
body: Builder(
builder: (context) => Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 2,
child: StreamBuilder(
builder: (context, AsyncSnapshot<User> snapshot) {
return CrudForm(
user: snapshot.data,
onUserAdded: (user) {
userService.addUser(user);
},
);
},
stream: userService.userObservable,
),
),
Expanded(
child: Text("Future button widget"),
),
Expanded(
flex: 3,
child: StreamBuilder(
builder: (ctx, AsyncSnapshot<List<User>> snap) {
return CrudList(
onUserSelected: userService.userSelected,
users: snap.data,
);
},
stream: userService.usersObservable,
),
),
],
),
),
),
);
}
void onEditUser(User user) {
setState(() {
user = user;
});
}
}
Приведенный выше виджет объединяет три упомянутых мною виджета.
Вот дочерний виджет:
Форма:
import 'package:andplus_flutter_7_gui/model/user.dart';
import 'package:flutter/material.dart';
class CrudForm extends StatefulWidget {
CrudForm({Key key, this.onUserAdded, this.user}) : super(key: key);
final User user;
final void Function(User user) onUserAdded;
_CrudFormState createState() => _CrudFormState(user: user);
}
class _CrudFormState extends State<CrudForm> {
_CrudFormState({this.user});
User user = User();
var _key = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Container(
child: Builder(
builder: (context) => Container(
color: Colors.blueAccent[100],
child: Form(
key: _key,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
Text(
"First Name",
style: TextStyle(fontSize: 20),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
initialValue: widget.user?.firstName == null ||
widget.user.firstName.isEmpty
? user.firstName
: widget.user.firstName,
validator: (value) {
if (value.isEmpty) {
return "First name is required";
}
return null;
},
onSaved: (value) {
setState(() {
user.firstName = value;
});
},
),
),
)
],
),
Row(
children: <Widget>[
Text(
"Last Name",
style: TextStyle(fontSize: 20),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
validator: (value) {
if (value.isEmpty) {
return "Last name is required";
}
return null;
},
onSaved: (value) {
setState(() {
user.lastName = value;
});
},
),
),
),
],
),
RaisedButton(
child: Text(
"Save",
),
splashColor: Colors.blueGrey,
onPressed: () {
if (!_key.currentState.validate()) {
return;
}
_key.currentState.save();
widget.onUserAdded(
new User(
firstName: user.firstName,
lastName: user.lastName,
),
);
},
)
],
),
),
),
),
),
);
}
}
Вот мой виджет списка.
import 'package:andplus_flutter_7_gui/model/user.dart';
import 'package:flutter/material.dart';
class CrudList extends StatefulWidget {
CrudList({Key key, this.users, this.onUserSelected}) : super(key: key);
final List<User> users;
final SelectUser onUserSelected;
_CrudListState createState() => _CrudListState();
}
class _CrudListState extends State<CrudList> {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.green,
child: ListView.builder(
itemCount: widget.users?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
var user = widget.users[index];
return ListTile(
key: Key(index.toString()),
title: Center(
child: Text(
"${user.firstName} ${user.lastName}",
style: TextStyle(color: Colors.white),
),
),
onTap: () {
print("${widget.users[index]} $index");
widget.onUserSelected(widget.users[index]);
},
);
},
),
);
}
}
typedef void SelectUser(User user);
И просто для дальнейшего контекста, вот мой пользовательский сервис, отвечающий за добавление объектов в поток и использование построителя потока в rxdart для уведомления об изменениях состояния.
import 'package:andplus_flutter_7_gui/model/user.dart';
import 'package:rxdart/rxdart.dart';
class UserService {
User _editedUser = User();
List<User> _users = <User>[];
BehaviorSubject<User> _userSubject;
BehaviorSubject<List<User>> _usersSubject;
UserService(this._editedUser) {
_userSubject = BehaviorSubject<User>.seeded(_editedUser);
_usersSubject = BehaviorSubject<List<User>>.seeded(_users);
}
Observable<List<User>> get usersObservable => _usersSubject.stream;
Observable<User> get userObservable => _userSubject.stream;
addUser(User user) {
_users.add(user);
_usersSubject.add(_users);
}
dispose() {
_userSubject.close();
_usersSubject.close();
}
void userSelected(User user) {
_editedUser = user;
_userSubject.add(_editedUser);
}
}
Чего мне не хватает?Похоже, мой виджет перестраивается и пытается установить начальное значение в форме, когда я нажимаю на пользователя в виджете списка.Но фактическое поле не обновляется, и я не уверен, почему.
Буду признателен за любую документацию или статьи о том, как лучше подходить к управлению данными и состояниями между родственными виджетами в рамках флаттера.