Вы можете использовать flutter_form_blo c.
Каждое поле имеет метод addError
, и вы можете вызывать его где угодно, в вашем случае это будет в методе onSubmitting
после получение ответа от сервера.
class MyFormBloc extends FormBloc<String, String> {
final email = TextFieldBloc();
MyFormBloc() {
addFieldBlocs(fieldBlocs: [email]);
}
@override
void onSubmitting() async {
// Awesome logic...
username.addError('That email is taken. Try another.');
}
}
Вы также можете иметь асинхронные валидаторы со временем отката
class MyFormBloc extends FormBloc<String, String> {
final username = TextFieldBloc(
asyncValidatorDebounceTime: Duration(milliseconds: 300),
);
MyFormBloc() {
addFieldBlocs(fieldBlocs: [username]);
username.addAsyncValidators([_checkUsername]);
}
Future<String> _checkUsername(String username) async {
await Future.delayed(Duration(milliseconds: 500));
if (username.toLowerCase() != 'flutter dev') {
return 'That username is already taken';
}
return null;
}
}
Вот небольшая демонстрационная программа, которую вы можете запустить, и учебник находится в форма бл c веб-сайт
pubspe c .yaml
dependencies:
flutter_form_bloc: ^0.11.0
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
void main() => runApp(App());
class App extends StatelessWidget {
const App({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: SubmissionErrorToFieldForm(),
);
}
}
class SubmissionErrorToFieldFormBloc extends FormBloc<String, String> {
final username = TextFieldBloc();
SubmissionErrorToFieldFormBloc() {
addFieldBlocs(
fieldBlocs: [
username,
],
);
}
@override
void onSubmitting() async {
print(username.value);
await Future<void>.delayed(Duration(milliseconds: 500));
if (username.value.toLowerCase() == 'dev') {
username.addError(
'Cached - That username is taken. Try another.',
isPermanent: true,
);
emitFailure(failureResponse: 'Cached error was added to username field.');
} else {
username.addError('That username is taken. Try another.');
emitFailure(failureResponse: 'Error was added to username field.');
}
}
}
class SubmissionErrorToFieldForm extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => SubmissionErrorToFieldFormBloc(),
child: Builder(
builder: (context) {
final formBloc =
BlocProvider.of<SubmissionErrorToFieldFormBloc>(context);
return Theme(
data: Theme.of(context).copyWith(
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
),
),
),
child: Scaffold(
appBar: AppBar(title: Text('Submission Error to Field')),
body: FormBlocListener<SubmissionErrorToFieldFormBloc, String,
String>(
onSubmitting: (context, state) {
LoadingDialog.show(context);
},
onSuccess: (context, state) {
LoadingDialog.hide(context);
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => SuccessScreen()));
},
onFailure: (context, state) {
LoadingDialog.hide(context);
Scaffold.of(context).showSnackBar(
SnackBar(content: Text(state.failureResponse)));
},
child: SingleChildScrollView(
physics: ClampingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
TextFieldBlocBuilder(
textFieldBloc: formBloc.username,
keyboardType: TextInputType.multiline,
decoration: InputDecoration(
labelText: 'Username',
prefixIcon: Icon(Icons.sentiment_very_satisfied),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('"dev" will add a cached error'),
),
RaisedButton(
onPressed: formBloc.submit,
child: Text('SUBMIT'),
),
],
),
),
),
),
),
);
},
),
);
}
}
class LoadingDialog extends StatelessWidget {
static void show(BuildContext context, {Key key}) => showDialog<void>(
context: context,
useRootNavigator: false,
barrierDismissible: false,
builder: (_) => LoadingDialog(key: key),
).then((_) => FocusScope.of(context).requestFocus(FocusNode()));
static void hide(BuildContext context) => Navigator.pop(context);
LoadingDialog({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Center(
child: Card(
child: Container(
width: 80,
height: 80,
padding: EdgeInsets.all(12.0),
child: CircularProgressIndicator(),
),
),
),
);
}
}
class SuccessScreen extends StatelessWidget {
SuccessScreen({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.tag_faces, size: 100),
SizedBox(height: 10),
Text(
'Success',
style: TextStyle(fontSize: 54, color: Colors.black),
textAlign: TextAlign.center,
),
SizedBox(height: 10),
RaisedButton.icon(
onPressed: () => Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (_) => SubmissionErrorToFieldForm())),
icon: Icon(Icons.replay),
label: Text('AGAIN'),
),
],
),
),
);
}
}