I want to copy user inputs to list1 to another list say list2 and save it, then if user inputs something again that should be inserted in list1 and then again its content should be copied to list2.
Output:
let say user inputs A,B,C then it should be stored in list1.
list1=['A','B','C']
and list2 will be list2=['A','B','C']
then if again user inputs something let's say D,E,F then list1 will be
list1=['D','E','F']
and list2 will be list2=['A','B','C','D','E','F']
and i want that list2 contents on gridview
Here is what i have done:
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
class Result extends StatefulWidget {
final Function resetHandler;
final int resultScore;
List textList = [];
Result(this.resultScore, this.textList, this.resetHandler);
#override
_ResultState createState() => _ResultState();
}
class _ResultState extends State<Result> {
List textListCopy;
var resultList;
int index = 0;
String get resultPhrase {
if (widget.resultScore <= 8) {
return 'You are awesome and innocent!';
} else if (widget.resultScore <= 12) {
return 'Pretty likeable!';
} else if (widget.resultScore <= 16) {
return 'You are .... Strange?!';
} else {
return 'You are so Bad!';
}
}
Color colorSelect() {
Color color;
if (widget.textList[0] == 'Blue' &&
widget.textList[1] == 'Rabbit' &&
widget.textList[2] == 'A') {
color = Colors.blue;
} else if (widget.textList[0] == 'Blue' &&
widget.textList[1] == 'Snake' &&
widget.textList[2] == 'B') {
color = Colors.green;
} else if (widget.textList[1] == 'Lion') {
color = Colors.orange;
} else if (widget.textList[2] == 'C') {
color = Colors.yellow;
} else {
color = Colors.white;
}
return color;
}
String indexSelection() {
list2.addAll(widget.list1); //i am copying my list1 to list2 here
print(list2);
return list2[0];
}
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
/*24 is for notification bar on Android*/
final double itemHeight = (size.height - kToolbarHeight - 24) / 2;
final double itemWidth = size.width / 2;
return Scaffold(
body: //Center(
//child:
//Column(
// mainAxisAlignment: MainAxisAlignment.center,
//children: <Widget>[
Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(color: colorSelect()),
child: //Center(
//child:
Column(children: [
Text(
resultPhrase,
//'Hello!!',
style: TextStyle(fontSize: 36, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
ElevatedButton(
onPressed: widget.resetHandler,
child: Text(
'Restart Quiz',
style: TextStyle(fontSize: 28),
textAlign: TextAlign.center,
)),
Expanded(
child: GridView.count(
childAspectRatio: (itemWidth / itemHeight),
controller: new ScrollController(keepScrollOffset: false),
shrinkWrap: true,
padding: EdgeInsets.fromLTRB(10, 20, 10, 0),
scrollDirection: Axis.vertical,
crossAxisCount: 3,
mainAxisSpacing: 10,
children: [
Card(
child: Text(
indexSelection(),
//index += 1;
//textList[index],
//index += 1;
textAlign: TextAlign.center,
),
),
],
))
])) //)
//]) //)
);
}
}
Another file
#override
Widget build(BuildContext context) {
return MaterialApp(
// Hide the debug banner
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: Text('My First App'),
),
body: _questionIndex < _questions.length
? Quiz(
answerQuestion: _answerQuestion,
questionIndex: _questionIndex,
questions: _questions,
)
: Result(_total_score, textList, _resetQuiz))); //here i'm calling _resetQuiz function
void _resetQuiz() {
setState(() {
_questionIndex = 0;
_total_score = 0;
list1= []; //here i'm emptying my list1 everytime when RESET QUIZ button is pressed so that the final screen color changes in the basis of my answer selection
});
Update:
i have tried from.list too, but every when user input something it only display that input, its not storing previous input
String indexSelection() {
print(list1);
list2= List.from(list1);
print(list2);
return list2[0];
}
Try this:
list2.addAll(list1);
Here's a good article about other ways to achieve the same.
You can use
List list2 = [...list1];
Related
I'm trying to do a search history using a search delegate but I'm having a problem.
When I perform a search, that element can appear several times in the history and what I want is that it not be repeated.
If I search 3 times for the same person, in the search history it appears 3 times
And I only want it to appear once.
How could I do it?
help would be appreciated.
Code and image::
class MPState extends State<MP>{
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: new Scaffold(
resizeToAvoidBottomInset : false,
appBar: AppBar(
title: Text("App"),
backgroundColor: Colors.redAccent,
elevation: 0,
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () async{
final busqueda = await showSearch(
context: context,
delegate: SearchDelegateMP("Buscar...",this.historialMenuPrincipal)
);
if(busqueda != null ) {
if (this.historialMenuPrincipal.length > 0) {
// this.historialMenuPrincipal.add(busqueda);
/*historialMenuPrincipal.removeWhere((item) =>
item.email == busqueda.email); // si elimina*/
for (int i = 0; i < historialMenuPrincipal.length; i++) {
if(historialMenuPrincipal[i].email== busqueda.email){
print(historialMenuPrincipal[i].email);
break;
}else{
this.historialMenuPrincipal.add(busqueda);
break;
}
}
}else{ this.historialMenuPrincipal.add(busqueda);}
}
}
class SearchDelegateMPextends SearchDelegate<SearchDelegateM>{
#override
List<Widget> buildActions(BuildContext context) {
return [
//code
];
}
#override
Widget buildResults(BuildContext context) {
//code
}
Widget buildSuggestions(BuildContext context) {
return StatefulBuilderSuggestions(context,this.historialMenuPrincipal);
}
Widget StatefulBuilderSuggestions(BuildContext context ,List <SearchDelegateM> historialMenuPrincipal){
return Container(
child:StatefulBuilder(
builder:(context,setState)
{
return Container(
child: ListView.builder(
itemCount: historialMenuPrincipal.length,
itemBuilder: (context,i)
{
contentPadding: EdgeInsets.symmetric(vertical: 12,horizontal: 16);
leading:CircleAvatar(
radius: 32,
backgroundImage: NetworkImage(
"https://2.bp.blogspot.com/-3ZzNt8ZsjQk/WR9W4IFn4II/AAAAAAAAAJw/_inTVynhS60V7F5IZ-461-pda7WArTStwCEw/s1600/ANA.jpg"),
);
return
ListTile(
title: Text(historialMenuPrincipal[i].email ),
trailing: IconButton(
icon: Icon(Icons.cancel,color: Colors.black,),
onPressed: () {
setState(() {
historialMenuPrincipal.remove(historialMenuPrincipal[i]);
});
},)
);
}
),
);
}
)
);
}
enter image description here
Empty your list with every new search, before you start adding to it.
this.historialMenuPrincipal.clear();
What is happening is that the result is being added n number of times, even if the result is already there from previous searches.
N = the number of times the search is matched.
List can have repeated elements. You can parse your list to Set as set only contains unique elements.
List <SearchDelegateM> uniqueElementsList = historialMenuPrincipal.toSet().toList();
use this code before showing your elements in Listview.builder() and use uniqueElementsList in your builder.
The idea behind my code is to populate a Column widget with dynamic data I get from a list of objects
I use a List>> that's essentially just a list of objects.
My List :
List<List<Map<String, String>>> listCardsAll = [
[
{
"cardCate": "AMEX",
"cardDesc": "American Platinum 6",
"cardImg":
"images/AMEX/American-Express®-Green-Card-Earn-Rewards-Points.png"
}
],
[
{
"cardCate": "AMEX",
"cardDesc": "American Platinum 5",
"cardImg":
"images/AMEX/American-Express®-Green-Card-Earn-Rewards-Points.png"
}
],
];
Then I call a function within a column that returns a Widget(I want to return a List but it gives an error)
Widget:
Column(
children: <Widget>[
Row(
children: <Widget>[generateCardsALL()],
)
],
)
I Use a for-loop then in the function a make a widget with that dynamic data but it only returns one widget rather than the Collection that i was hoping for.
my Function :
Widget generateCardsALL() {
var count = 0;
for (var item in listCardsAll) {
count++;
if (count <= 2) {
return ReusableCards(
cardName: item[0]["cardDesc"],
cardLink: item[0]["cardImg"],
cardRowNum: count,
);
} else {
count = 0;
return ReusableCards(
cardName: item[0]["cardDesc"],
cardLink: item[0]["cardImg"],
cardRowNum: 3,
);
}
}
}
(The cardRowNum is only to see when its 3 cards next to each other that it can generate a new row)
This is the ReusableCards
ReusableCards:
class ReusableCards extends StatelessWidget {
final String cardLink;
final int cardRowNum;
final String cardName;
ReusableCards(
{#required this.cardLink,
#required this.cardRowNum,
#required this.cardName});
#override
Widget build(BuildContext context) {
// addToListcards(cardLink);
print("Inserted");
return (cardRowNum == 1 || cardRowNum == 2
? GestureDetector(
onDoubleTap: () {},
child: Padding(
padding: EdgeInsets.fromLTRB(0, 15, 15, 20),
child: Container(
width: MediaQuery.of(context).size.width * 0.24,
child: SizedBox(
child: Column(
children: <Widget>[
Image.asset(cardLink, fit: BoxFit.fill),
Text(cardName)
],
),
),
),
),
)
: cardRowNum == 3
? Padding(
padding: EdgeInsets.fromLTRB(0, 15, 0, 20),
child: Container(
width: MediaQuery.of(context).size.width * 0.24,
child: SizedBox(
child: Column(
children: <Widget>[
Image.asset(cardLink, fit: BoxFit.fill),
Text("" + cardRowNum.toString())
],
),
),
),
)
: SizedBox());
}
}
if I want to make it a List rather then just a widget it breaks. Can someone please help me with this dilemma
Try using map for iterating through the list and getting a list of widgets,
Example:
Column(
children: listCardsAll.map((oneCard){
return Container(
child: Text(oneCard.cardDesc)
);
}).toList(),
)
I think the problem is that inside of the function generateCardsALL. You don't return an array of Widgets, you should change to this.
List<Widget> generateCardsALL() {
var count = 0;
List<Widget> widgets;
for (var item in listCardsAll) {
count++;
if (count <= 2) {
widgets.add(ReusableCards(
cardName: item[0]["cardDesc"],
cardLink: item[0]["cardImg"],
cardRowNum: count,
));
} else {
count = 0;
widgets.add(ReusableCards(
cardName: item[0]["cardDesc"],
cardLink: item[0]["cardImg"],
cardRowNum: 3,
));
}
}
return widgets;
}
I'm almost done with my Flutter/Dart project by creating a quiz app. The only dart file I'm having a big challenge is what I named the quizpage file. Below is the exactcode:
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hausa_quiz/resultpage.dart';
import 'dart:math';
class getjson extends StatelessWidget {
// accept the alphabet as a parameter
String alphabet;
getjson(this.alphabet);
String assettoload;
// a function
// sets the asset to a particular JSON file
// and opens the JSON
setasset() {
if (alphabet == "Harafin A") {
assettoload = "assets/A.json";
} else if (alphabet == "Harafin B") {
assettoload = "assets/B.json";
} else if (alphabet == "Harafin C") {
assettoload = "assets/C.json";
} else if (alphabet == "Harafin D") {
assettoload = "assets/D.json";
} else if (alphabet == "Harafin F") {
assettoload = "assets/F.json";
} else if (alphabet == "Harafin G") {
assettoload = "assets/G.json";
} else if (alphabet == "Harafin H") {
assettoload = "assets/H.json";
} else if (alphabet == "Harafin I") {
assettoload = "assets/I.json";
} else if (alphabet == "Harafin J") {
assettoload = "assets/J.json";
} else if (alphabet == "Harafin K") {
assettoload = "assets/K.json";
} else if (alphabet == "Harafin L") {
assettoload = "assets/L.json";
} else if (alphabet == "Harafin M") {
assettoload = "assets/M.json";
} else if (alphabet == "Harafin N") {
assettoload = "assets/N.json";
} else if (alphabet == "Harafin R") {
assettoload = "assets/R.json";
} else if (alphabet == "Harafin S") {
assettoload = "assets/S.json";
} else if (alphabet == "Harafin T") {
assettoload = "assets/T.json";
} else if (alphabet == "Harafin U") {
assettoload = "assets/U.json";
} else if (alphabet == "Harafin W") {
assettoload = "assets/W.json";
} else if (alphabet == "Harafin Y") {
assettoload = "assets/Y.json";
} else {
assettoload = "assets/Z.json";
}
}
#override
Widget build(BuildContext context) {
// this function is called before the build so that
// the string assettoload is available to the DefaultAssetBuilder
setasset();
// and now we return the FutureBuilder to load and decode JSON
return FutureBuilder(
future:
DefaultAssetBundle.of(context).loadString(assettoload, cache: true),
builder: (context, snapshot) {
List mydata = json.decode(snapshot.data.toString());
if (mydata == null) {
return Scaffold(
body: Center(
child: Text(
"Loading",
),
),
);
} else {
return quizpage(mydata: mydata);
}
},
);
}
}
class quizpage extends StatefulWidget {
var mydata;
quizpage({Key key, #required this.mydata}) : super(key: key);
#override
_quizpageState createState() => _quizpageState(mydata);
}
class _quizpageState extends State<quizpage> {
var mydata;
_quizpageState(this.mydata);
Color colortoshow = Colors.indigoAccent;
Color right = Colors.green;
Color wrong = Colors.red;
int marks = 0;
int i = 1;
// extra varibale to iterate
int j = 1;
int timer = 30;
String showtimer = "30";
Map<String, Color> btncolor = {
"a": Colors.indigoAccent,
"b": Colors.indigoAccent,
"c": Colors.indigoAccent,
};
bool canceltimer = false;
// overriding the initstate function to start timer as this screen is created
#override
var random_array = [1, 2, 3, 4, 5, 6, 7, 8, 9,10];
void initState() {
// var random_array;
var distinctIds = [];
var rand = new Random();
for (int i = 0; ;) {
distinctIds.add(rand.nextInt(12));
random_array = distinctIds.toSet().toList();
if(random_array.length <10){
continue;
}else{
break;
}
}
starttimer();
super.initState();
}
// overriding the setstate function to be called only if mounted
#override
void setState(fn) {
if (mounted) {
super.setState(fn);
}
}
void starttimer() async {
const onesec = Duration(seconds: 1);
Timer.periodic(onesec, (Timer t) {
setState(() {
if (timer < 1) {
t.cancel();
nextquestion();
} else if (canceltimer == true) {
t.cancel();
} else {
timer = timer - 1;
}
showtimer = timer.toString();
});
});
}
void nextquestion() {
canceltimer = false;
timer = 30;
setState(() {
if (j < 10) {
i = random_array[j];
j++;
} else {
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => resultpage(marks: marks),
));
}
btncolor["a"] = Colors.indigoAccent;
btncolor["b"] = Colors.indigoAccent;
btncolor["c"] = Colors.indigoAccent;
});
starttimer();
}
void checkanswer(String k) {
// in the previous version this was
// mydata[2]["1"] == mydata[1]["1"][k]
// which i forgot to change
// so nake sure that this is now corrected
if (mydata[2][i.toString()] == mydata[1][i.toString()][k]) {
// just a print sattement to check the correct working
// debugPrint(mydata[2][i.toString()] + " is equal to " + mydata[1][i.toString()][k]);
marks = marks + 10;
// changing the color variable to be green
colortoshow = right;
} else {
// just a print sattement to check the correct working
// debugPrint(mydata[2]["1"] + " is equal to " + mydata[1]["1"][k]);
colortoshow = wrong;
}
setState(() {
// applying the changed color to the particular button that was selected
btncolor[k] = colortoshow;
canceltimer = true;
});
// changed timer duration to 1 second
Timer(Duration(seconds: 1), nextquestion);
}
Widget choicebutton(String k) {
return Padding(
padding: EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 20.0,
),
child: MaterialButton(
onPressed: () => checkanswer(k),
child: Text(
mydata[1][i.toString()][k],
style: TextStyle(
color: Colors.white,
fontFamily: "Oswald",
fontSize: 20.0,
),
maxLines: 2,
),
color: btncolor[k],
splashColor: Colors.indigo[700],
highlightColor: Colors.indigo[700],
minWidth: 200.0,
height: 45.0,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
),
);
}
#override
Widget build(BuildContext context) {
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitDown, DeviceOrientation.portraitUp]);
return WillPopScope(
onWillPop: () {
return showDialog(
context: context,
builder: (context) =>
AlertDialog(
title: Text(
"HausaQuiz",
),
content: Text("Ba zaka iya komawa baya ba a daidai nan!"),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
'To',
),
)
],
));
},
child: Scaffold(
body: Column(
children: <Widget>[
Expanded(
flex: 3,
child: Container(
padding: EdgeInsets.all(15.0),
alignment: Alignment.bottomLeft,
child: Text(
mydata[0][i.toString()],
style: TextStyle(
fontSize: 20.0,
fontFamily: "Oswald",
),
),
),
),
Expanded(
flex: 6,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
choicebutton('a'),
choicebutton('b'),
choicebutton('c'),
],
),
),
),
Expanded(
flex: 1,
child: Container(
alignment: Alignment.topCenter,
child: Center(
child: Text(
showtimer,
style: TextStyle(
fontSize: 35.0,
fontWeight: FontWeight.w700,
fontFamily: 'Oswald',
),
),
),
),
),
],
),
),
);
}
}
The highlighted code below is where I believe my problem is: The app is crashing and returning this exact error:
The following assertion was thrown building FutureBuilder<String>(state: _FutureBuilderState<String>#42556):
type 'List' is not a subtype of type 'List'**
See where I think the problem is:
var random_array = [1, 2, 3, 4, 5, 6, 7, 8, 9,10];
void initState() {
// var random_array;
var distinctIds = [];
var rand = new Random();
for (int i = 0; ;) {
distinctIds.add(rand.nextInt(12));
random_array = distinctIds.toSet().toList();
if(random_array.length <10){
continue;
}else{
break;
}
}
Also note that if I remove the int numbers in var random_array = [1, 2, 3, 4, 5, 6, 7, 8, 9,10] and leave like var random_array = [], the app will run but once it reaches like 5, 6 questions, it'll crash, wait for some seconds and continue with the questions making the whole process get lost.
This is what I exactly want to do:
I've an average of 200 questions in each of the json file I created and put in the asset folder. I want to write a code that can generate random questions say, 10 random questions from the 200 questions in the json file. I want the questions randomised.
I am trying to list categories of products in my app, and i am using provider package for state management. During the build time its shows the list is empty even though it is not, then I add a click event to update the list that time it works.
I call the getAllCategories() function during the splash screen and the vallCategoryList has values.
This is my category class
class Category with ChangeNotifier{
int catId;
String catName;
String catThumbnail;
List<SetCategory> allCategoryList = [];
void getAllCategories() async {
String categoryUrl = 'https://app.ecwid.com/api/ccccccc';
Response allCategory = await get(categoryUrl);
print('getAllCategories');
if (allCategory.statusCode == 200) {
var categoryData = allCategory.body;
int totalcount = jsonDecode(categoryData)['count'];
if (allCategoryList.length != totalcount) {
allCategoryList.clear();
for (int i = 0; i < totalcount; i++) {
allCategoryList.add(SetCategory(
catId: jsonDecode(categoryData)['items'][i]['id'],
catName: jsonDecode(categoryData)['items'][i]['name'],
catThumbnail: jsonDecode(categoryData)['items'][i]['thumbnailUrl'],
));
}
}
}
print('allcategorylist length ${allCategoryList.length}');
notifyListeners();
}
}
class SetCategory {
int catId;
String catName;
String catThumbnail;
SetCategory(
{ this.catId, this.catName, this.catThumbnail});
}
My code for screen
class HomeScreen extends StatefulWidget {
static const String id = 'homeScreen';
// static int reload = 0;
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
final category = Provider.of<Category>(context);
print('category length ${category.allCategoryList.length}'); // here it shows length as 0 even though it has a value of 16.
return Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Category ${category.allCategoryList.length}',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 15.0,
),
textAlign: TextAlign.start,
),
),
InkWell(
onTap: () {
category.getAllCategories(); // when i tap here this updates the list
}),
],
),
Did you use ChangeNotifierProvider in your Widget as shown here
If you just used Provider it is not updating but just makes the object accessible from the descendants
Solved by adding a Consumer, changed code like this
child: Consumer<Category>(
builder: (_,category,__){
return ListView.builder();
}
i have a class that return a widget that create a text field i want call this class in a for loop in other class and every times that call this class text field set different value for my text fields and show text fields in list view how done it and next question about text field is when i set a value to text field and go to next page and back to my page value of text field cleaned and dont show how access to value of it this is my class that create a text field object
import 'package:art_man/components/Utility/Keys.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class InputText extends StatefulWidget {
String _hint, id;
double height;
TextEditingController ctrl;
Color brdercolor;
double brderwidth;
double radius;
double margintop;
TextAlign textAlign;
int maxlines;
Color hintconlor;
double hintsize;
double maxlenght;
TextInputType keyboardtype;
FontWeight fontWeight;
TextEditingController controller;
InputText(this._hint, this.id,
{this.height,
this.brdercolor,
this.brderwidth,
this.margintop,
this.radius,
this.textAlign,
this.maxlines,
this.hintconlor,
this.hintsize,
this.maxlenght,
this.keyboardtype,
this.fontWeight,
this.controller});
#override
myInputText createState() {
myInputText it = new myInputText(id,_hint,
height: height,
brdercolor: brdercolor,
brderwidth: brderwidth,
margintop: margintop,
radius: radius,
maxlines: maxlines,
hintconlor: hintconlor,
alignment: textAlign,
hintsize: hintsize,
maxlenght: maxlenght,
keyboardtype: keyboardtype,
fontwidth : fontWeight,
controller: controller);
return it;
}
}
class myInputText extends State<InputText> {
String _hint;
String id;
double height;
Color brdercolor;
double brderwidth;
double radius;
double margintop;
TextAlign alignment;
int maxlines;
Color hintconlor;
double hintsize;
double maxlenght;
TextInputType keyboardtype;
FontWeight fontwidth;
TextEditingController controller ;
myInputText(this.id,this._hint,
{this.height,
this.brdercolor,
this.brderwidth,
this.margintop,
this.radius,
this.alignment,
this.maxlines,
this.hintsize,
this.hintconlor,
this.maxlenght,
this.keyboardtype,
this.fontwidth,
this.controller});
#override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
margin: EdgeInsets.only(
top: margintop == null ? 1.0 : margintop,
),
padding: EdgeInsets.only(right: 3),
height: height == null ? 40.0 : height,
decoration: BoxDecoration(
border: Border.all(
color: brdercolor == null ? Colors.white : brdercolor,
width: brderwidth == null ? 0.0 : brderwidth),
color: brdercolor == null ? Colors.white : brdercolor,
borderRadius: BorderRadius.circular(radius == null ? 25 : radius)),
child: TextField(
controller: controller,
keyboardType: keyboardtype==null?TextInputType.text:keyboardtype,
textInputAction: TextInputAction.next,
inputFormatters: [
new LengthLimitingTextInputFormatter(maxlenght==null?
30:maxlenght.toInt()),
],
onChanged: (value){
Kelid.setter(id, value);
print(Kelid.getter(id));
},
textAlign: alignment == null ? TextAlign.right : alignment,
maxLines: maxlines==null?1:maxlines,
style: TextStyle(
fontSize: hintsize==null?14:hintsize,
),
textDirection: TextDirection.rtl,
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: const EdgeInsets.all(0.0),
hintText: _hint,
errorStyle: TextStyle(height: 0),
hintStyle: TextStyle(
fontWeight: fontwidth==null?FontWeight.normal:fontwidth,
color: hintconlor == null ? Colors.grey : hintconlor,
fontSize: hintsize == null ? 13 : hintsize)),
),
);
}
}
From what I understand, you're looking into generating TextField dynamically and be able to store its values. What you can do here is store TextField values using state management available in Flutter. In this approach, I've used the provider package to store the TextField values and are saved on every text change. With the TextField values stored, we can easily restore the values on the TextFields.
Complete sample
import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (BuildContext context) => TextFieldDetailsModel(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: ListView.builder(
itemCount: 20,
itemBuilder: (BuildContext context, int index) {
return _inputText('Hint for index $index', index);
}),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// Print all TextField String stored in Provider
// List<String> listText =
// Provider.of<TextFieldDetailsModel>(context, listen: false)
// ._textFieldContent;
// for (int x = 0; x < listText.length; x++) {
// debugPrint('Text: ${listText[x]}, index: $x');
// }
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
child: Icon(Icons.play_arrow),
),
);
}
Widget _inputText(String hint, int index) {
var _textEditingController = TextEditingController();
return Container(
padding: EdgeInsets.all(16.0),
child: Consumer<TextFieldDetailsModel>(builder: (BuildContext context,
TextFieldDetailsModel txtModel, Widget? child) {
// If Provider List contains value, prefill the TextField
if (txtModel._textFieldContent.length > 0 &&
txtModel._textFieldContent.length > index &&
txtModel._textFieldContent[index].trim() != '') {
_textEditingController.text = txtModel._textFieldContent[index];
// set TextField cursor at the end
_textEditingController.selection = TextSelection.collapsed(offset: _textEditingController.text.length);
}
return TextField(
controller: _textEditingController,
onChanged: (String str) {
// Only add String if TextField has value
if (str.trim().isEmpty)
txtModel.remove(index);
else
txtModel.add(str, index);
},
decoration: InputDecoration(hintText: hint),
);
}),
);
}
}
// https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple
class TextFieldDetailsModel extends ChangeNotifier {
final List<String> _textFieldContent = [];
UnmodifiableListView<String> get text =>
UnmodifiableListView(_textFieldContent);
void add(String text, int? index) {
if (index != null)
_textFieldContent.fillAndSet(index, text);
else
_textFieldContent.add(text);
// This call tells the widgets that are listening to this model to rebuild.
notifyListeners();
}
void remove(int index) {
_textFieldContent.removeAt(index);
notifyListeners();
}
void removeAll() {
_textFieldContent.clear();
// This call tells the widgets that are listening to this model to rebuild.
notifyListeners();
}
}
// Create filler for skipping ranges
// Source: https://stackoverflow.com/a/65504227/2497859
extension ListFiller<T> on List<String> {
void fillAndSet(int index, String value) {
if (index >= this.length) {
this.addAll(List<String>.filled(index - this.length + 1, ''));
}
this[index] = value;
}
}
class SecondRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Go back!'),
),
),
);
}
}
Demo
The values can also be stored only on page change. Create a List<TextEditingController> to be assigned on the TextFields generated.
late List<TextEditingController> _listTextEditingController = [];
...
Widget _inputText(String hint, int index) {
var _textEditingController = TextEditingController();
_listTextEditingController.add(_textEditingController);
...
}
On page change (i.e. button trigger), extract all the List values and store it in the Provider.
for (int x = 0; x < _listTextEditingController.length; x++) {
Provider.of<TextFieldDetailsModel>(context, listen: false).add(_listTextEditingController[x].text, x);
}