I have voter id api and I want show the show specific voter details input from the textfield eg if the user put in text field voter name and voter show the all details in next page specific voteruser details
1. Create a TextEditController, which is a class you can link to the input field to get the text inside it (it’s good practice to put it inside a StatefulWidget, in this way we can dispose it):
// Define a custom Form widget.
class MyCustomForm extends StatefulWidget {
const MyCustomForm({Key? key}) : super(key: key);
#override
_MyCustomFormState createState() => _MyCustomFormState();
}
// Define a corresponding State class.
// This class holds the data related to the Form.
class _MyCustomFormState extends State<MyCustomForm> {
// Create a text controller and use it to retrieve the current value
// of the TextField.
final myController = TextEditingController();
#override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
// Fill this out in the next step.
}
}
2. Link the controller with your text field:
TextField(
controller: myController,
);
3. Get text with:
myController.text
4. Implementing Navigator class, send data as argument of your next page following the official documentation:
Basic Navigator implementation: https://flutter.dev/docs/cookbook/navigation/navigation-basics
Pass arguments to the next screen: https://flutter.dev/docs/cookbook/navigation/navigate-with-arguments
Related
I am new to flutter, and have some difficulties understanding how to correctly pass parameters to a widget that is navigated to.
My goal is, that when the users clicks on a button, I want to start up a wizard controller with a certain enum parameter based on what button the user clicked.
The wizard controller has an app bar but the primary content is a dynamic child wizard flow widget which is chosen based on the enum parameter. The wizard controller (and its children) needs to be stateful because it, among other things, holds information about the current page in the chosen wizard flow and a model which holds data for the whole wizard flow.
As far as I can see there are two options of instantiating the wizard controller with the enum parameter:
Option 1.
//Pass the parameters when the route is pushed
onPressed: () {
Navigator.pushNamed(context, '/wizard', arguments: EFlowType.WizardFlow2);
},
//In the build method, extract the parameter from the navigator, and use it here:
class WizardController extends StatefulWidget {
WizardController({Key? key}) : super(key: key);
#override
State<WizardController> createState() => _WizardControllerState();
}
class _WizardControllerState extends State<WizardController> {
StatefulWidget? dynamicWidget;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
final flowType = ModalRoute.of(context)!.settings.arguments as EFlowType;
switch (flowType) {
case EFlowType.WizardFlow1:
//Prepare models, set dynamicWidget and do alot of work
break;
case EFlowType.WizardFlow2:
//Prepare models, set dynamicWidget and do alot of work
break;
}
return Scaffold(
appBar: AppBar(
title: Text('Wizard controller'),
),
body: dynamicWidget
);
}
....
The problem is here that I would like to access the flowType parameter in the initState (or in the WizardController class constructor), so I dont need to do all the initialization work every time the widget is being rebuild/UI is updated.
If I try to access the flowtype in the initstate I get this error which I cannot come around: FlutterError (dependOnInheritedWidgetOfExactType<_ModalScopeStatus>() or dependOnInheritedElement() was called before _WizardControllerState.initState() completed.
It is not possible to access the context in the WizardController class, so that is not an option.
Option 2.
In the MaterialApp method I can declare an extra onGenerateRoute setting, for these routes in my app that has parameters:
class WizardController extends StatefulWidget {
EFlowType flowType = EFlowType.MeterChange;
WizardController({Key? key, required this.flowType}) : super(key: key);
#override
State<WizardController> createState() => _WizardControllerState();
}
onGenerateRoute: (RouteSettings settings) {
if (settings.name == '/wizard') {
return MaterialPageRoute(builder: (_) => WizardController(flowType: settings.arguments as EFlowType));
}
This makes the flowType available in the initState. But why on earth would I declare context and logic-specific stuff where I am defining my routes? Is this a preferred way of defining widget parameters?
So how would you normally go around this quite normal problem?
The widget TrainsPage is added to the build graph in main.dart, when the corresponding menu button is clicked. This is done twice: once when _routes is empty and a second time when _routes is filled.
Widget pageSelector() {
if (_selectedIndex == 2) {
return new TrainsPage(routes: _routes);
} else
return Text("");
}
In TrainsPage.dart, I have the code for the stateful widget TrainsPage.
class TrainsPage extends StatefulWidget {
const TrainsPage({Key? key, required this.routes}) : super(key: key);
final List<RSRoute> routes;
#override
_TrainsPageState createState() => _TrainsPageState();
}
class _TrainsPageState extends State<TrainsPage> {
List<RSRoute> _routes = List.empty();
#override
void initState() {
super.initState();
this._routes = new List<RSRoute>.from(widget.routes);
Now, the second time, TrainsPage gets called in main.dart (now with routes filled), initState() of _TrainsPageState is not called, which is responsible to read the data in routes. And because routes was empty the first time, there is nothing in display on the trains page.
Why does TrainsPage not rebuild _TrainsPageState, when it clearly got new data in the constructor?
This is exactly why the State exists : to keep the state of the current context alive even when the widget is rebuild.
If it was recreated each time the statefull widget is rebuild it could not keep the state of its own variables.
class MyWidget extends StatelessWidget {
var _someStateVariable = 0;
#override
void build(BuildContext context){
// here an action that increment _someStateVariable
}
}
Here _someStateVariable would be reset to 0 at each rebuild. Or if we wanted a StateFullWidget in the first place it's because we'll update this variable later and want to keep its updated value through the multiple widget rebuilds.
If you don't have such state variable to maintain maybe you don't need a StateFullWidget here.
Now to the solution to your problem : you can override didUpdateWidget instead of initstate since it will be called at each widget rebuild :
#override
void didUpdateWidget() {
didUpdateWidget();
_routes = new List<RSRoute>.from(widget.routes);
}
I have a MainScreen() file where I defined two Widgets, side by side:
Left menu (ListTiles).
Main View (rest of screen, to the right).
My issue is that I need to tap the ListTiles on the left and dynamically change the Widget loaded on the Main View.
Any suggestions?
The main view must be stateful for this to work, as you need to trigger a rerender once you tap one of the tiles. You want to pass a callback function from your main screen to your menu of list tiles.
Your best bet is to setup an enum and have each listtile provide the callback with its own enum value.
Once you have the callback in your list of tiles, you can easily pass it onward to the individual tiles. Then, execute the callback function onTap and provide it with the correct enum value.
On your main screen, you just show the correct widget by looking at the current enum value.
class MainScreen extends StatefulWidget {
const MainScreen({Key? key}) : super(key: key);
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
MyEnum _currentTile = MyEnum.Home;
void _callbackFunction(MyEnum tileOption) {
setState(() {
_currentTile = tileOption;
});
}
#override
Widget build(BuildContext context) {
return MyCustomListTile((MyEnum tile) => _callbackFunction(tile) );
}
}
How do I preserve my data when I navigate between two different tabs in Flutter... Basically what I want to achieve is, on one side of the tab, I see users data, and on the other side of the tab, I want to be able to copy some of those user data and paste into some textfields on the other tab without losing the data in the text fields when I navigate back and forth and still also preserving the level of scrolling I might have done on the tab where the users data show.
You need to use AutomaticKeepAliveClientMixin This will ensure the State instance is not destroyed when leaving the screen.Extend your every tabview's state class with it and create an override method wannaKeepAlive and set it to true.Here is an example of a widget using AutomaticKeepAliveClientMixin.
class Example extends StatefulWidget {
#override
_ExampleState createState() => _ExampleState();
}
class _ExampleState extends State<Example> with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
return Container(
);
}
#override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
}
My app uses a set of Card()s inside a PageView(), each card has multiple text lines.
The user gives input for each line (e.g. modifying the text etc.).
I want to keep this input for a while.
This is how my code looks at the moment, abstractly speaking:
class MyCard extends StatefulWidget {
final List<Widget> _myLines = [];
#override
State<StatefulWidget> createState() => MyCardState();
}
class MyCardState extends State<MyCard> {
...
#override
Widget build(BuildContext context) {
...
widget._myLines.add(ChangeNotifierProvider(
create: (context) => MyLineModel(context, lineText),
child: RecipeLine())
...
}
}
This doesn't work well:
As soon as I swipe left / right through the PageView onto other cards and then swipe back, the Card is being built again. This also leads to a rebuild of MyLineModel, which in turn erases all the user's input.
How can I avoid the rebuild of MyLineModel and keep the user's input?
You can solve this in 2 ways:
Create the model outside the widget and pass the model for the widget, as a variable to a constructor for example, or using Provider or any other technique of Dependency Injection.
Using any of the KeepAlive APIs, such as the AutomaticKeepAliveClientMixin as:
class MyCardState extends State<MyCard> with AutomaticKeepAliveClientMixin {
...
#override
bool get wantKeepAlive => true;
}
As the official documentation states, we can simply use the ChangeNotifierProvider.value() constructor...
In the original code:
class MyCardState extends State<MyCard> {
...
#override
Widget build(BuildContext context) {
...
widget._myLines.add(ChangeNotifierProvider.value(
value: MyLineModel(context, lineText),
child: RecipeLine())
...
}
}