I am trying to create a form with multiple tabs but for some reason if i call validate or save on form i can get only values from tab that is active and same is true for error i think it may be because form only gets values from fields that are currently rendered on screen.
so can some one tell me how can i make form work with multiple tab-view so that after changing tab i can validate tabs that have't been visited and also from vested ones as well.
there is AutomaticKeepAliveClientMixin but it can only keep state alive but i am more interested in onSave or validator as i am managing state in parent element not in tabviews
Thanks in advance
For now what I am using is a mix of pageview and tab view for pages instead of default flutter pageview I am useing this package preload_page_view (as in flutter default page view there is no option for preloading but once a page is loaded we can tell flutter to save it so this package actually provide an option on how many pages should be preloaded etc.)
and then Tabbar for switching pages like this
class IssuesEditScreen extends StatefulWidget {
static final routeName = "/issues_edit";
@override
_IssuesEditScreenState createState() => _IssuesEditScreenState();
}
class _IssuesEditScreenState extends State<IssuesEditScreen>
with SingleTickerProviderStateMixin {
final GlobalKey<FormState> _form = GlobalKey();
final _scaffold = GlobalKey<ScaffoldState>();
Issue _instance;
TabController _tabController;
PreloadPageController _pageController = PreloadPageController(
initialPage: 0, keepPage: true, viewportFraction: 0.99);
bool _canChange = true;
bool _loading = false;
Map<String, String> _errors = {};
Map<String, dynamic> _formData = {};
@override
void dispose() {
super.dispose();
_tabController.dispose();
_pageController.dispose();
}
@override
void initState() {
// TODO: implement initState
_tabController = TabController(length: 3, vsync: this);
_tabController.addListener(() {
if (_tabController.indexIsChanging) {
changePage(_tabController.index, page: true);
}
});
super.initState();
}
void _submit() {
if (!_form.currentState.validate()) {
_scaffold.currentState
.showSnackBar(SnackBar(content: Text("Please resolve given errors")));
return;
}
_formData.clear();
_form.currentState.save();
}
void changePage(index, {page = false, tab = false}) async {
if (page) {
_canChange = false;
await _pageController.animateToPage(index,
duration: Duration(milliseconds: 500), curve: Curves.ease);
_canChange = true;
} else {
_tabController.animateTo(index);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffold,
appBar: AppBar(
title: Text("Form"),
bottom: TabBar(
controller: _tabController,
tabs: [
Tab(
text: "Page1",
),
Tab(
text: "Page2",
),
Tab(text: "Page3"),
],
),
actions: [
FlatButton(
child: Text("Save"),
onPressed: __submit)
],
),
body: Form(
key: _form,
child: PreloadPageView(
preloadPagesCount: 5,
physics: AlwaysScrollableScrollPhysics(),
controller: _pageController,
onPageChanged: (index) {
if (_canChange) {
changePage(index);
}
},
children: [
Page1(formData: _formData, errors: _errors,),
Page2(formData: _formData, errors: _errors),
Page3(formData: _formData, errors: _errors)
],
),
),
);
}
}
class Page1 extends StatelessWidget {
const Page1 ({
Key key,
@required Map<String,dynamic > formData,
@required Map<String, String> errors,
}) : _formData = formData, _errors = errors, super(key: key);
final Map<String,dynamic > _formData;
final Map<String, String> _errors;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
elevation: 3,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
child: SingleChildScrollView(
child: Column(
children: [
TextFormField(
onSaved: (value) =>
_formData['field1'] = value,
decoration: BorderInputDecorator(
errorText: _errors['field1'],
label: "Field1",
),
validator: (value) {
if (value.isEmpty) {
return "This Field is required";
}
return null;
},
),
],
),
),
)
)
)
);
}
}
as you can see with that you can use onsave validators and add more page just can save or validate on form to get all data in _submit
There can be syntax issue