flutter-form-builder-ecosystem / flutter-form-builder-ecosystem/flutter_form_builder
[General]: Improve focus behavior when validating a FormBuilderTextField when autovalidateMode is not set to AutovalidateMode.disabled
- Dominant language
- Dart
- Stars
- 1.6k
- Forks
- 561
- PR merge metrics
- No merged PRs in 30d
Description
### Is there an existing issue for this?
- [X] I have searched the existing issues
### Package/Plugin version
10.0.0-dev.1
### Platforms
- [X] Android
- [X] iOS
- [ ] Linux
- [ ] MacOS
- [ ] Web
- [ ] Windows
### Flutter doctor
Flutter doctor
```bash
[√] Flutter (Channel stable, 3.27.1, on Microsoft Windows [Version 10.0.26100.2605], locale en-US)
[√] Windows Version (Installed version of Windows is version 10 or higher)
[√] Android toolchain - develop for Android devices (Android SDK version 35.0.0)
[√] Chrome - develop for the web
[√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.10.4)
[√] Android Studio (version 2024.1)
[√] VS Code (version 1.96.2)
[√] Connected device (5 available)
[√] Network resources
```
### Minimal code example
Code sample
```dart
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
final RouteObserver routeObserver = RouteObserver();
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
navigatorObservers: [routeObserver],
home: FormScreen(),
);
}
}
class FormScreen extends StatefulWidget {
const FormScreen({super.key});
@override
State createState() => _FormScreenState();
}
class _FormScreenState extends State {
@override
Widget build(BuildContext context) {
// final size = MediaQuery.of(context).size;
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Form Builder Example'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: FormChild(),
),
),
);
}
}
class FormChild extends StatefulWidget {
const FormChild({super.key});
@override
State createState() => _FormChildState();
}
class _FormChildState extends State with ScopedGetItMixin {
final GlobalKey _formBuilderKey = GlobalKey();
AutovalidateMode _autovalidateMode = AutovalidateMode.disabled;
@override
Widget build(BuildContext context) {
return FormBuilder(
key: _formBuilderKey,
autovalidateMode: _autovalidateMode,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
spacing: 10.0,
children: [
// Submit button
ElevatedButton(
style: ButtonStyle(shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(5.0)))),
onPressed: () {
FocusManager.instance.primaryFocus?.unfocus();
},
child: const Text('Unfocus'),
),
ElevatedButton(
style: ButtonStyle(shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(5.0)))),
onPressed: () {
final result = _formBuilderKey.currentState?.saveAndValidate();
if(result != true && _autovalidateMode != AutovalidateMode.onUserInteraction){
setState(() {
_autovalidateMode = AutovalidateMode.onUserInteraction;
});
}
},
child: const Text('submit'),
),
ElevatedButton(
style: ButtonStyle(shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(5.0)))),
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
showDragHandle: true,
builder: (context) {
return SizedBox(
height: 500,
width: 400,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
FormBuilderTextField(
name: 'textfield2',
decoration: const InputDecoration(
labelText: 'Enter text 1',
),
validator: FormBuilderValidators.required(),
)
],
),
);
},
);
},
child: Text("Open bottom sheet"),
),
],
),
),
// TextField 1
FormBuilderTextField(
name: 'textfield1',
decoration: const InputDecoration(
labelText: 'Enter text 1',
),
validator: FormBuilderValidators.required(),
),
const SizedBox(height: 20),
],
),
);
}
}
mixin ScopedGetItMixin on State implements RouteAware {
@override
void didChangeDependencies() {
super.didChangeDependencies();
routeObserver.subscribe(this, ModalRoute.of(context)!);
}
@override
void dispose() {
super.dispose();
routeObserver.unsubscribe(this);
}
@override
void didPop() {
}
@override
void didPopNext() {
}
@override
void didPush() {
}
@override
void didPushNext() {
}
}
```
### Current Behavior
I've developed a mixin called ScopedGetItMixin for use with StatefulWidget that contains a FormBuilder. However, I've noticed that after the form calls saveAndValidate, tapping a button to open a showModalBottomSheet causes a FormBuilderTextField to unexpectedly regain focus, triggering the keyboard to appear.
The main cause might be ScopedGetItMixin, but at the moment, I’m not sure why using ScopedGetItMixin results in this behavior.
### Expected Behavior
I hope the focus behavior of FormBuilderTextField gets fixed.
### Steps To Reproduce
I will describe it again through the video below.
https://github.com/user-attachments/assets/da71d071-a19c-4aa6-af25-12d8b3b6b73e
### Aditional information
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.