Sending multiple controllers as one controller - flutter

class OtpScreen extends StatefulWidget {
const OtpScreen({Key? key}) : super(key: key);
#override
State<OtpScreen> createState() => _OtpScreenState();
}
class _OtpScreenState extends State<OtpScreen> {
late String otp1;
late String otp2;
late String otp3;
late String otp4;
late String otp5;
late String otp6;
late OtpModel _futureotp;
final _formKey = GlobalKey<FormState>();
late TextEditingController controller;
#override
void initState() {
controller = TextEditingController();
controller.addListener(() {
_onChanged();
});
super.initState();
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
void _onChanged() {
try {
dynamic sendotp = ApiHelper().otpValidation();
if (sendotp is OtpModel) {
setState(() {
_futureotp = sendotp;
});
}
if (sendotp.status == 'success') {
Routes.sailor.navigate(
'/signIn',
navigationType: NavigationType.pushAndRemoveUntil,
removeUntilPredicate: (routes) => false,
transitions: [SailorTransition.fade_in],
);
} else {
print(sendotp.message);
}
} catch (e) {
print(e);
}
}
[1
I'm trying to create an OTP screen with textformfield and I have 6 TextEditingControllers but I want to send the value I get from them as one controller. How can I do that? and How would you do it if you were to create an otp screen that sends data to the backend without a button?
p.s: I tried creating 6 controllers and putting an adlistener at the last controller.

If you want to use in-built dart functionality, then try using Expando feature. It basically allows you to attach values to existing objects. Now this may not be answer that you are looking for but it certainly helps to know more than one way to tackle an issue.
If you want to know more about Expando feature than consider checking out this question.
However, you should be careful while adding an object to Expando as they effectively stays alive forever. This is what it says in official docs:
There is no restriction on other classes, even for compile time constant objects. Be careful if adding expando properties to compile time constants, since they will stay alive forever.

you can try pinput, or check their implementation and try to make your own one.

Related

Create / Manage dynamic TextEditingControllers

I got a ListView.builder that generates n number of elements and I am looking at adding a controller for each of them. I have seen some approaches of adding a controller to a list of controllers and then access them by the index however I am just wondering how will this impact the performance of the screen if lets say you have 20 controllers? Are there some best practices for this scenario? Should you even go down this line or avoid it?
I suggest to introduce a Widget for all items in list.
Make sure you dispose in a correct place for the performance.
Also I request to store the user entered value with the object of item will help to restore on scrolls.
Eg:
class YourWidget extends StatefulWidget {
const YourWidget({Key? key, required this.item}) : super(key: key);
final YourItem item;
#override
State<YourWidget> createState() => _YourWidgetState();
}
class _YourWidgetState extends State<YourWidget> {
final controller = TextEditingController();
#override
void initState() {
super.initState();
controller.text = widget.item.enteredValue;
}
#override
Widget build(BuildContext context) {
return TextField(
controller: controller,
onChanged: (value){
widget.item.enteredValue = value;
},
...
);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
}
class YourItem {
String? id;
...
String enteredValue = '';
}

When should I override debugFillProperties in Flutter?

Why should I use debugFillProperties in the stateful widget in Flutter? I have seen Some flutter Built-in stateful widgets Like Slider using this method.
I went through Flutter Docs given here. I'm still not able to understand the practical usage of debugFillProperties. When or why use it?
I tried It in My example code, Still Not able to understand. I did not find any docs or relevant information was found about this method in Flutter.
double? min;
#override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
print("--------------Debug-------------");
properties.add(IntProperty('min', min, defaultValue: 5, ifNull: "nulll"));
}
By overriding the debugFillProperties function, we can show the current value of a variable (state) in the Details Tree
example:
without overriding this method:
class _MyHomePageState extends State<MyHomePage> {
late MyColorScheme _myColorScheme;
int _counter = 0;
#override
void initState() {
_myColorScheme = MyColorScheme(Colors.black, Colors.white);
super.initState();
}
}
with overriding this method:
class _MyHomePageState extends State<MyHomePage> {
late MyColorScheme _myColorScheme;
int _counter = 0;
#override
void initState() {
_myColorScheme = MyColorScheme(Colors.black, Colors.white);
super.initState();
}
#override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(IntProperty('_counter', _counter));
properties.add(
DiagnosticsProperty<MyColorScheme>('myColorScheme', _myColorScheme));
}
}
Also, the information from debugFillProperties will be shown in the output from a debugDumpApp call.
See Debugging App Programmatically -> Widget Tree section for more details.

SignalR client flutter library shows null values in another widget when using Flutter Provider

Am trying to connect on a SignalR server with flutter, and I tried to use Provider to access the data which comes to another Widget, but am receiving a null value. This is the SignalR library for flutter am using.
Below is my Class which extends a ChangeNotifier :
class BreakingNewsSignalRClient with ChangeNotifier {
String _breakingNews;
Future<void> getBreakingNews() async {
final hubConnection = HubConnectionBuilder()
.withUrl('https://services.xxxx.com/breakingNews')
.build();
await hubConnection.start();
hubConnection.on("UpdateBreakingNews", (data){
_breakingNews = data.toString();
print(data.toString());
notifyListeners();
});
hubConnection.onclose((error) => print("Connection Closed"));
}
String get breakingNewsFunction => _breakingNews;
}
class BreakingNewsModel {
final String News;
BreakingNewsModel(this.News);
}
As you can see the code above, am having a getter String get breakingNewsFunction => _breakingNews;, this is called in another Stateful Widget to populate some data from the SignalR server, but it returns null in the other widget, however, when you try to print data here in the getBreakingNews method the data is shown.
Below is another Widget class which receives the data :
class BreakingNews extends StatefulWidget {
const BreakingNews({Key key, #required this.article, #required this.index})
: super(key: key);
final Article article;
final int index;
#override
_BreakingNewsState createState() => _BreakingNewsState();
}
class _BreakingNewsState extends State<BreakingNews> {
settings() async{
var breakingNewsInfo =
Provider.of<BreakingNewsSignalRClient>(context, listen: false);
await breakingNewsInfo.getBreakingNews();
print('lets see -- ${breakingNewsInfo.breakingNewsFunction}');
}
#override
void initState() {
// TODO: implement initState
settings();
super.initState();
}
}
So when you look at this line print('lets see -- ${breakingNewsInfo.breakingNewsFunction}');, it prints null, am still wondering what am doing wrong here.
Kindly need some help.
Did you try data[0]?
Can you write it?
_breakingNews = data[0].toString();
instead of
_breakingNews = data.toString();
This is pretty simple. Your Future<void> getBreakingNews() async returns void and therefore null. Just adjust the return type to whatever you need.
Edit:
Actually, the problem is you are not calling your getter here.
await breakingNewsInfo.getBreakingNews();
So either return your result from the function, or call the getter. Either should work.

Transfer variable to other class

I am trying to get a list from class to the other. But I want it to only be transferred after it has got a value assigned from a Future. Is there a way to do so (something like a setState method that acts across classes) My code is here:
class Design extends StatefulWidget {
#override
_DesignState createState() => _DesignState();
}
class _DesignState extends State<Design>{
var Data;
#override
void initState() {
super.initState();
comparer().then((List returnedV){
setState(() {
Data = returnedV;
});
});
}
Future<List> compare() async {
...
return dataDevice
}
}
class AboutSheet extends StatefulWidget {
final List Data;
AboutSheet({#required this.Data});
#override
_AboutSheetState createState() => _AboutSheetState();
}
class _AboutSheetState extends State<AboutSheet> {
}
Every time I use the variable Data in the second class it has the value null. I think it's because I have defined it before with the value null and it's pulling that and is not waiting for the future to assign a value to it. I can't think of a workaround. I would really appreciate your help!
What you are referring to is a state management solution. There is a lot of them, with each their pros and cons. I (and the Flutter team) would suggest Provider.
Take a look at this : List of state management approaches

initialize data once in initState and call the setState when data is ready causes exception

Since flutter calls the build method many times in different condition, to avoid getting the data many times, I initialize the data in initState.
I want to re-build the widget when the data is ready.
Here is my code :
class Test extends StatefulWidget {
#override
_TestState createState() => new _TestState();
}
class _TestState extends State<Test> {
Data data;
bool dataReady = false;
#override
void initState() {
super.initState();
getData(context).then((Data data) async {
setState(() {
dataReady= true;
});
});
}
#override
Widget build(BuildContext context) {
if (dataReady) {
return createMainContent(context);
} else {
return new Container();
}
}
}
However, it results in following exception :
inheritFromWidgetOfExactType(_InheritedProvider) or inheritFromElement() was called before _TestState.initState() completed.
May I know am I doing something wrong here?
When I add the following line to implementation of getData(context)
await Future.delayed(new Duration(milliseconds: 300));
the exception does not happen.
For everyone coming here at a later point
It is best to use the #override void didChangeDependencies () method of the State class.
From the docs
This method is also called immediately after initState. It is safe to call BuildContext.inheritFromWidgetOfExactType from this method.
But make sure to check if you have already performed your initialization
#override
void didChangeDependencies() {
super.didChangeDependencies();
if (bloc == null) { // or else you end up creating multiple instances in this case.
bloc = BlocProvider<MyBloc>.of(context);
}
}
Edit: Better answer below.
Apparently, you cannot access getData(context) during initState (more concrete: before it completed).
The reason, so I believe, is that getData tries to look up an InheritedWidget ancestor up in the tree, but the tree is just now being built (your widget is created during the parent widget's build).
The obvious solution would be to delay getData's lookup to a later point in time. There are several ways to achieve that:
Delay the lookup to a later time. scheduleMicrotask should work fine.
Look it up during the first build call. You could have an isInitialized field set to false and in you build, something like:
if (!isInitialized) {
isInitialized = true;
// TODO: do the getData(...) stuff
}
an alternative is to put it inside PostFrameCallback which is between initState and Build.
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) => getData());
super.initState();
}
getData() async {
}
I moved my code to my build method from initState and it worked
class _TestState extends State<Test> {
Data data;
bool dataReady = false;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
getData(context).then((Data data) async {
setState(() {
dataReady= true;
});
});
if (dataReady) {
return createMainContent(context);
} else {
return new Container();
}
}
}