I start with block.
I'm trying to create a form with a read mode and an edit/create.
I arrive with the WillPopScope
widget to manage the rollback depending on the mode.
Only my form doesn't find its initial state: it remains to modify whereas I do not save the modification.
And on the back event to the list this one is not up to date.
Thanks,
Edit : I think that problem with state because when I pass in edition or back from edition to read : nothing state is happens why ? How back from edition to read and get initial value ?
class FamilyDetailsPage extends StatelessWidget {
const FamilyDetailsPage({Key? key, required this.family}) : super(key: key);
final Family family;
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => FamilyBloc(repository: FamilyRepository()),
child: FamilyDetailsView(family: family),
);
}
}
class FamilyDetailsView extends StatefulWidget {
const FamilyDetailsView({Key? key, required this.family}) : super(key: key);
final Family family;
@override
State<FamilyDetailsView> createState() => FamilyDetailsViewState();
}
class FamilyDetailsViewState extends State<FamilyDetailsView>
with SingleTickerProviderStateMixin {
late FamilyBloc _familyBloc;
late bool isUpdated;
late Family _currentFamily;
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
static final GlobalKey<FormState> _formKey =
GlobalKey<FormState>(debugLabel: '_appFamilyFormState');
@override
void initState() {
super.initState();
_currentFamily = widget.family;
isUpdated = (_currentFamily.id != 0) ? false : true;
_familyBloc = BlocProvider.of<FamilyBloc>(context);
_familyBloc.add(FetchFamilyEvent(item: _currentFamily));
}
@override
void dispose() {
_familyBloc.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
if (!isUpdated || _currentFamily.id == 0) {
return true;
}
setState(() => isUpdated = false);
return false;
},
child: Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: const Text('Family'),
actions: <Widget>[
!isUpdated
? IconButton(
onPressed: () => setState(() => isUpdated = true),
icon: const Icon(Icons.edit))
: IconButton(
onPressed: () {
_saveFamily();
setState(() => isUpdated = false);
},
icon: const Icon(Icons.save),
),
],
),
body: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0), child: _buildForm())),
),
);
}
void _saveFamily() {
if (_formKey.currentState != null) {
if (_formKey.currentState!.validate()) {
_familyBloc.add(_currentFamily.id == 0
? CreateFamilyEvent(item: _currentFamily)
: UpdateFamilyEvent(item: _currentFamily));
}
}
}
Widget _buildForm() {
return BlocConsumer<FamilyBloc, FamilyState>(
listener: (context, state) {},
builder: (context, state) {
if (state is ItemLoadedState || state is IsSavedState) {
return Column(
children: <Widget>[
TextFormField(
readOnly: !isUpdated,
decoration: const InputDecoration(
labelText: 'Name', border: OutlineInputBorder()),
//controller: _cName,
initialValue: _currentFamily.name,
onChanged: (value) => _currentFamily.name = value,
validator: (value) {
if (value!.trim().isEmpty) {
return 'Name cannot be empty';
}
return null;
}),
],
);
}
if (state is FailState) {
print(state.message);
}
if (state is LoadingState) {
return const Center(
child: CircularProgressIndicator(),
);
}
return const Text('No data');
});
}
}
Bloc file :
class FamilyBloc extends Bloc<FamilyEvent, FamilyState> {
FamilyBloc({required this.repository}) : super(LoadingState()) {
on<BackEvent>(_onBackEvent);
on<FetchFamiliesEvent>(_onFetchFamilies);
on<FetchFamilyEvent>(_onFetchFamily);
on<UpdateFamilyEvent>(_onUpdateFamily);
on<CreateFamilyEvent>(_onCreateFamily);
}
FamilyRepository repository;
FutureOr<void> _onBackEvent(BackEvent event, Emitter<FamilyState> emit) {
emit(InitialState());
}
}
Event file :
abstract class FamilyEvent extends Equatable {
final Family? item;
const FamilyEvent({this.item});
@override
List<Object> get props => [];
}
class BackEvent extends FamilyEvent {}
State File :
abstract class FamilyState extends Equatable {
final Family? item;
final List<Family>? list;
const FamilyState({this.item, this.list});
@override
List<Object> get props => [];
}
class InitialState extends FamilyState {}
class FailState extends FamilyState {
const FailState({required this.message});
final String message;
}
class LoadingState extends FamilyState {}
class ListLoadedState extends FamilyState {
const ListLoadedState({required this.listFamily}) : super(list: listFamily);
final List<Family> listFamily;
}
class ItemLoadedState extends FamilyState {
const ItemLoadedState({required this.family}) : super(item: family);
final Family family;
}
class IsSavedState extends FamilyState {
const IsSavedState({required this.family});
final Family family;
}
I found the solution.
In my _buildForm
don't check the state InitialState
for get initial value.
if (state is InitialState) {
SchedulerBinding.instance.addPostFrameCallback((_) => _initForm());
return _buildForm();
}