5

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

kei nagae
  • 200
  • 1
  • 15

2 Answers2

1

I have the same need as you and I'm trying to manage the different forms with a Provider that has a List <GlobalKey <FormState>> formKeys = [GlobalKey <FormState> (), GlobalKey <FormState> () ...]; a key for each form. Then in a button on the tabbar onPressed: form.validate (formKey) for each form. If all forms are fine, save info, else error message.

radrow
  • 6,419
  • 4
  • 26
  • 53
  • well it could be done as well and in my work I had around 20 form and each form had around 20 25 fields and for first what i did was to use form as parent of tabview and pass all editing controllers to each separate tab and on save i will make validations and was saving all errors in errors map and then was using filed decoration to show errors – kei nagae Dec 26 '20 at 17:24
  • but then a found an other way that actually using pageview and for tabing i used tabbar and there is a package that can build all pages at once and then we can use form with out any issue and i am using this for the time being – kei nagae Dec 26 '20 at 17:28
  • can you share some code? An example with 2 tabs? – ctechdev.io Dec 26 '20 at 17:59
  • ok then i am writing a new answer bellow with example – kei nagae Dec 27 '20 at 10:26
  • It done have a look and feel free to ask any quetions – kei nagae Dec 27 '20 at 11:02
1

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

kei nagae
  • 200
  • 1
  • 15