docs(form): add example app
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:form_bloc_example/sign_up/widgets/sign_up_form.dart';
|
||||
|
||||
class SignUpPage extends StatelessWidget {
|
||||
const SignUpPage({Key? key}) : super(key: key);
|
||||
|
||||
static Route route() {
|
||||
return MaterialPageRoute<void>(builder: (_) => const SignUpPage());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Complex Form')),
|
||||
body: const Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: SingleChildScrollView(
|
||||
child: SignUpForm(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
// Copyright (C) 2022 WYATT GROUP
|
||||
// Please see the AUTHORS file for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:form_bloc_example/app/app.dart';
|
||||
import 'package:form_bloc_example/constants.dart';
|
||||
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
|
||||
|
||||
class _NameInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<FormDataCubit, FormDataState>(
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
onChanged: (name) => context
|
||||
.read<FormDataCubit>()
|
||||
.dataChanged(formFieldName, Name.dirty(name)),
|
||||
keyboardType: TextInputType.name,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'name',
|
||||
helperText: '',
|
||||
errorText:
|
||||
state.data.input(formFieldName).invalid ? 'invalid name' : null,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _EmailInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<FormDataCubit, FormDataState>(
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
onChanged: (email) => context
|
||||
.read<FormDataCubit>()
|
||||
.dataChanged(formFieldEmail, Email.dirty(email)),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'email',
|
||||
helperText: '',
|
||||
errorText: state.data.input(formFieldEmail).invalid
|
||||
? 'invalid email'
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PhoneInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<FormDataCubit, FormDataState>(
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
onChanged: (phone) => context
|
||||
.read<FormDataCubit>()
|
||||
.dataChanged(formFieldPhone, Phone.dirty(phone)),
|
||||
keyboardType: TextInputType.phone,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'phone',
|
||||
helperText: '',
|
||||
errorText: state.data.input(formFieldPhone).invalid
|
||||
? 'invalid phone'
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SirenInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<FormDataCubit, FormDataState>(
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
onChanged: (siren) => context
|
||||
.read<FormDataCubit>()
|
||||
.dataChanged(formFieldSiren, Siren.dirty(siren)),
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'siren',
|
||||
helperText: '',
|
||||
errorText: state.data.input(formFieldSiren).invalid
|
||||
? 'invalid SIREN'
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _IbanInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<FormDataCubit, FormDataState>(
|
||||
builder: (context, state) {
|
||||
return TextField(
|
||||
onChanged: (iban) => context
|
||||
.read<FormDataCubit>()
|
||||
.dataChanged(formFieldIban, Iban.dirty(iban)),
|
||||
keyboardType: TextInputType.text,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'iban',
|
||||
helperText: '',
|
||||
errorText:
|
||||
state.data.input(formFieldIban).invalid ? 'invalid IBAN' : null,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CheckHiddenInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
title: const Text('This input is not exported'),
|
||||
trailing: BlocBuilder<FormDataCubit, FormDataState>(
|
||||
builder: (context, state) {
|
||||
return Checkbox(
|
||||
value: state.data.input<bool>(formFieldHidden).value,
|
||||
onChanged: (v) {
|
||||
final value = v!; // state is false, so value can't be null
|
||||
|
||||
context.read<FormDataCubit>().dataChanged(
|
||||
formFieldHidden,
|
||||
Boolean.dirty(value: value),
|
||||
);
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CheckIsProInput extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
title: const Text('Are you a pro?'),
|
||||
trailing: BlocBuilder<FormDataCubit, FormDataState>(
|
||||
builder: (context, state) {
|
||||
return Checkbox(
|
||||
value: state.data.input<bool>(formFieldPro).value,
|
||||
onChanged: (isPro) {
|
||||
final value = isPro!; // state is false, so value can't be null
|
||||
|
||||
context.read<FormDataCubit>().dataChanged(
|
||||
formFieldPro,
|
||||
Boolean.dirty(value: value),
|
||||
);
|
||||
|
||||
if (value) {
|
||||
context.read<FormDataCubit>().updateFormData(
|
||||
App.getProFormData(),
|
||||
operation: SetOperation.union);
|
||||
} else {
|
||||
context.read<FormDataCubit>().updateFormData(
|
||||
App.getNormalFormData(),
|
||||
operation: SetOperation.intersection);
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SignUpButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<FormDataCubit, FormDataState>(
|
||||
buildWhen: (previous, current) => previous.status != current.status,
|
||||
builder: (context, state) {
|
||||
return state.status.isSubmissionInProgress
|
||||
? const CircularProgressIndicator()
|
||||
: ElevatedButton(
|
||||
onPressed: state.status.isValidated
|
||||
? () =>
|
||||
context.read<FormDataCubit>().submitForm()
|
||||
: null,
|
||||
child: const Text('SIGN UP'),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DebugButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<FormDataCubit, FormDataState>(
|
||||
builder: (context, state) {
|
||||
return ElevatedButton(
|
||||
onPressed: () {
|
||||
log(state.toString());
|
||||
},
|
||||
child: const Text('DEBUG'),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SignUpForm extends StatelessWidget {
|
||||
const SignUpForm({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocListener<FormDataCubit, FormDataState>(
|
||||
listener: (context, state) {
|
||||
if (state.status.isSubmissionSuccess) {
|
||||
Navigator.of(context).pop(); // Never happens here
|
||||
} else if (state.status.isSubmissionFailure) {
|
||||
ScaffoldMessenger.of(context)
|
||||
..hideCurrentSnackBar()
|
||||
..showSnackBar(
|
||||
SnackBar(content: Text(state.errorMessage ?? 'Sign Up Failure')),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: Align(
|
||||
alignment: const Alignment(0, -1 / 3),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_NameInput(),
|
||||
const SizedBox(height: 8),
|
||||
_EmailInput(),
|
||||
const SizedBox(height: 8),
|
||||
_PhoneInput(),
|
||||
const SizedBox(height: 8),
|
||||
_CheckHiddenInput(),
|
||||
const SizedBox(height: 8),
|
||||
_CheckIsProInput(),
|
||||
const SizedBox(height: 8),
|
||||
BlocBuilder<FormDataCubit, FormDataState>(
|
||||
builder: (context, state) {
|
||||
if (state.data.input<bool>(formFieldPro).value) {
|
||||
return Column(children: [
|
||||
_SirenInput(),
|
||||
const SizedBox(height: 8),
|
||||
_IbanInput(),
|
||||
const SizedBox(height: 8),
|
||||
]);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_SignUpButton(),
|
||||
const SizedBox(height: 8),
|
||||
_DebugButton(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user