Flutter - list inside a list builder - list

I don't have much experience with flutter.
I would like to use the language_tool library for Dart and Flutter.
I created the script below:
void main() => runApp(mainApp());
class mainApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Chat(),
);
}
}
class Chat extends StatefulWidget {
const Chat({Key? key}) : super(key: key);
#override
_ChatState createState() => _ChatState();
}
class _ChatState extends State<Chat> {
String text = 'Henlo i am Gabriele';
Future<List<WritingMistake>> tool(String text) async {
var tool = LanguageTool();
var result = tool.check(text);
var correction = await result;
print(correction);
List<WritingMistake> mistakes = [];
for (var m in correction) {
//var mistake = m.issueDescription;
WritingMistake mistake = WritingMistake(
message: m.message,
offset: m.offset,
length: m.length,
issueType: m.issueType,
issueDescription: m.issueDescription,
replacements: m.replacements,
);
mistakes.add(mistake);
}
print(mistakes.length);
print(mistakes);
return mistakes;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
color: Colors.red,
height: 150.0,
width: double.infinity,
child:
Center(child: Text(text, style: TextStyle(fontSize: 20.0))),
),
Text(
'\n Tap on the blue button to replace it in the message.\n',
),
FutureBuilder(
future: tool(text),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(
child: Text('Loading...'),
),
);
} else {
return SizedBox(
height: 200.0,
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
children: [
Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(' ' +
(index + 1).toString() +
' '),
Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(text.substring(
snapshot.data[index].offset,
snapshot.data[index].offset +
snapshot
.data[index].length)),
),
color: Colors.blue,
),
],
),
Icon(
Icons.arrow_forward,
),
Text(snapshot.data[index].issueDescription)
],
),
// Todo: Row with List of .replacements
],
),
),
// snapshot.data[index].issueDescription),
);
},
),
);
}
},
),
Expanded(child: Container(color: Colors.grey))
],
),
),
);
}
}
And it currently looks like this:
And I would like it to become like in this screen, but I would like the green containers derive from the WritingMistake.replacements list (with each element of the list as text):
Do you know how I can do it?

Spent several hours doing exactly what you wanted. My Code:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:language_tool/language_tool.dart';
void main() => runApp(mainApp());
class mainApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Chat(),
);
}
}
class Chat extends StatefulWidget {
const Chat({Key? key}) : super(key: key);
#override
_ChatState createState() => _ChatState();
}
class _ChatState extends State<Chat> {
String text = 'Henlo i am Gabriele';
static List<WritingMistake> mistakes = []; // Moved Here And Init Like Static
Future<List<WritingMistake>> tool(String text) async {
var tool = LanguageTool();
var result = tool.check(text);
var correction = await result;
print(correction);
for (var m in correction) {
//var mistake = m.issueDescription;
WritingMistake mistake = WritingMistake(
message: m.message,
offset: m.offset,
length: m.length,
issueType: m.issueType,
issueDescription: m.issueDescription,
replacements: m.replacements,
);
mistakes.add(mistake);
}
print(mistakes.length);
print(mistakes);
return mistakes;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
color: Colors.red,
height: 150.0,
width: double.infinity,
child: Center(
child: Text(text, style: const TextStyle(fontSize: 20.0))),
),
const Text(
'\n Tap on the blue button to replace it in the message.\n',
),
FutureBuilder(
future: tool(text),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return const Center(
child: Text('Loading...'),
);
} else {
return SizedBox(
height: 200.0,
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int mistakeIdIndex) {
return Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
children: [
Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(' ' +
(mistakeIdIndex + 1).toString() +
' '),
Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(text.substring(
snapshot.data[mistakeIdIndex]
.offset,
snapshot.data[mistakeIdIndex]
.offset +
snapshot
.data[mistakeIdIndex]
.length)),
),
color: Colors.blue,
),
],
),
const Icon(
Icons.arrow_forward,
),
Text(snapshot
.data[mistakeIdIndex].issueDescription)
],
),
// Todo: Row with List of .replacements
SizedBox(
width: MediaQuery.of(context)
.size
.width, // Width = Screen Width
height: 44,
child: ListView.builder(
itemCount: mistakes.length,
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemBuilder: (BuildContext context,
int replacementsListIndex) {
return buildReplacements(mistakeIdIndex,
replacementsListIndex);
}),
)
],
),
),
// snapshot.data[index].issueDescription),
);
},
),
);
}
},
),
Expanded(child: Container(color: Colors.grey))
],
),
),
);
}
Widget buildReplacements(int mistakeIdIndex, int replacementsListIndex) {
if (mistakes[replacementsListIndex].replacements!.length == null) {
return const SizedBox(); // Return Empty If No Replacements Found, Just To Be Safe
}
// Check For Mistake ID - Remove This Will Cause A Problem with Displaying All Replacements Under All Words
if (mistakeIdIndex == replacementsListIndex) {
// If Only 1 Replacement, No Need To Create ListView.builder
if (mistakes[replacementsListIndex].replacements!.length == 1) {
return Container(
margin: const EdgeInsets.all(4),
padding: const EdgeInsets.all(8),
decoration: const BoxDecoration(
color: Color.fromARGB(255, 174, 213, 130)), // Green Color
child: Text(
mistakes[replacementsListIndex].replacements!.toString(),
style: const TextStyle(),
textAlign: TextAlign.center,
),
);
}
// Create ListView.builder to Create A Split Between Replacements To One Wrong-Word (Every Replacement Has It's Own Container With Green Background)
else {
return ListView.builder(
itemCount: mistakes[replacementsListIndex].replacements!.length,
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemBuilder: (context, index) {
return Container(
margin: const EdgeInsets.all(4),
padding: const EdgeInsets.all(8),
decoration: const BoxDecoration(
color: Color.fromARGB(255, 174, 213, 130)), // Green Color
child: Text(
mistakes[replacementsListIndex]
.replacements![index]!
.toString(),
style: const TextStyle(),
textAlign: TextAlign.center,
),
);
});
}
}
// If Replacement Not For This Wrong-Word, Then Skip
else {
return const SizedBox();
}
}
}
And Screenshot:

Related

Adding words to the list in the correct order

I'm trying to adjust the words to form the sentence in the correct order. Like this. Or like Duolingo sentence examples. (without drag and drop, just lists and/or maps)
Sentence:
static String mySentence = "My cat is black";
static List<String> correctWords = mySentence.split(" ");
static List<String> shuffleWords = [...correctWords]..shuffle();
Green part: shuffleWidgetList. A widget has been added to each word in the shuffleWords.
List<Widget> shuffleWidgetList = [];
generateShuffleWidgets() {
for (int i = 0; i < shuffleWords.length; i++) {
shuffleWidgetList.add(shuffleWordWidget(shuffleWords[i]));
}
}
Blue part: answerWidgetList.
List<Widget> answerWidgetList = [];
White part: The answer is checked with the button.
I am comparing the list of words (List<String>) correctWords with answerWords.
onTap: () {
if (IterableEquality().equals(correctWords, answerWords)) {
print("correct");
} else {
print("wrong");
}
},
Steps:
1. In the green part, I choose a word from the list. The item I clicked on is added to the answerWidgetList (blue part). And removed from shuffleWidgetList (green part).
onTap: () {
setState(() {
shuffleWidgetList.remove(shuffleWordWidget(word));
//not necessary remove. Look at Problem 1.a
answerWidgetList.add(answerWordWidget(word));
answerWords.add(word);
});
},
2. Clicking on a word in the blue part adds it to its old place in the shuffleWidgetList(green part). And it is removed from answerWidgetList (blue part).
onTap: () {
setState(() {
shuffleWidgetList.add(shuffleWordWidget(word)); //Problem 1.b
answerWidgetList.remove(answerWordWidget(word)); //Problem 2
answerWords.remove(word);
});
},
Problems:
1.a. How can I change shuffleWordWidget's text color when I click an item in shuffleWidgetList?
I tried: In the shuffleWidgetList(green part) it is unnecessary to remove the item when clicking. Text should be transparent (to keep the widget size) and not be clicked again. For this I added bool clicked=false; to shuffleWordWidget. When clicked, onTap() updates to true, but color doesn't update. Like this:
shuffleWordWidget(String word) {
bool clicked = false;
return InkWell(
child: Container(
color: clicked == false ? Colors.white54 : Colors.grey,
child: Text(
word,
style: TextStyle(fontSize: 12,color:clicked == false ? Colors.black : Colors.transparent ),
),
),
onTap: () {
if (clicked == false) {
setState(() {
...
clicked = true;
});
}
},
);
}
1.b. How can I add shuffleWidgetList to its old place when an item is clicked in answerWidgetList?
For old place; that if the user clicks on a word from the answerWidgetList(blue part), it should be the same word clicked=false; again in the shuffleWidgetList(green part).
2. How can I remove item from answerWidgetList when I click an item in answerWidgetList?
answerWidgetList.remove(answerWordWidget(word)); doesn't work.
My approach may be completely wrong. You can also suggest different solutions.
Full code:
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Sentence Creation',
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
static String mySentence = "My cat is black";
static List<String> correctWords = mySentence.split(" ");
static List<String> shuffleWords = [...correctWords]..shuffle();
List<String> answerWords = [];
List<Widget> shuffleWidgetList = [];
List<Widget> answerWidgetList = [];
generateShuffleWidgets() {
for (int i = 0; i < shuffleWords.length; i++) {
shuffleWidgetList.add(shuffleWordWidget(shuffleWords[i]));
}
}
#override
void initState() {
super.initState();
generateShuffleWidgets();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
flex: 2,
child: Container(
color: Colors.blue,
child: Center(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: answerWidgetList,
),
),
)),
Expanded(
child: Container(
color: Colors.green,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: shuffleWidgetList,
),
),
),
SizedBox(
height: 50,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
child: Center(child: Text("Check")),
onTap: () {
if (IterableEquality().equals(correctWords, answerWords)) {
print("correct");
} else {
print("wrong");
}
print("correct list: $correctWords");
print("answer list: $answerWords");
},
),
),
)
],
),
);
}
shuffleWordWidget(String word) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: InkWell(
child: Container(
color: Colors.white54,
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Text(
word,
style: TextStyle(fontSize: 12),
),
),
),
onTap: () {
setState(() {
shuffleWidgetList.remove(shuffleWordWidget(word));
//not necessary remove. Look at Problem 1.a
answerWidgetList.add(answerWordWidget(word));
answerWords.add(word);
});
},
),
);
}
answerWordWidget(String word) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: InkWell(
child: Container(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Text(
word,
style: TextStyle(fontSize: 12),
),
),
),
onTap: () {
setState(() {
shuffleWidgetList.add(shuffleWordWidget(word)); //Problem 1.b
answerWidgetList.remove(answerWordWidget(word)); //Problem 2
answerWords.remove(word);
});
},
),
);
}
}
Put this code in _HomePageState.
static String mySentence = "My cat is black";
static List<String> correctWords = mySentence.split(" ");
static List<String> shuffleWords = [...correctWords]..shuffle();
List<String> answerWords = [];
List<Widget> shuffleWidgetList = [];
List<Widget> answerWidgetList = [];
generateShuffleWidgets() {
for (int i = 0; i < shuffleWords.length; i++) {
shuffleWidgetList.add(shuffleWordWidget(shuffleWords[i],true));
}
}
#override
void initState() {
super.initState();
generateShuffleWidgets();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
flex: 2,
child: Container(
color: Colors.blue,
child: Center(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: answerWidgetList,
),
),
)),
Expanded(
child: Container(
color: Colors.green,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: shuffleWidgetList,
),
),
),
SizedBox(
height: 50,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
child: Center(child: Text("Check")),
onTap: () {
if (IterableEquality().equals(correctWords, answerWords)) {
print("correct");
} else {
print("wrong");
}
print("correct list: $correctWords");
print("answer list: $answerWords");
},
),
),
)
],
),
);
}
shuffleWordWidget(String word,bool visible) {
return Visibility(
visible: visible,
child: Padding(
padding: const EdgeInsets.all(4.0),
child: InkWell(
child: Container(
color: Colors.white54,
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Text(
word,
style: TextStyle(fontSize: 12),
),
),
),
onTap: () {
setState(() {
//shuffleWidgetList.remove(shuffleWordWidget(word));
int i = shuffleWords.indexOf(word);
shuffleWidgetList[i]=shuffleWordWidget(word,false);
//not necessary remove. Look at Problem 1.a
answerWidgetList.add(answerWordWidget(word));
answerWords.add(word);
});
},
),
),
);
}
answerWordWidget(String word) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: InkWell(
child: Container(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Text(
word,
style: TextStyle(fontSize: 12),
),
),
),
onTap: () {
setState(() {
int i = shuffleWords.indexOf(word);
// shuffleWidgetList.insert(i,shuffleWordWidget(word)); //Problem 1.b
shuffleWidgetList[i]=shuffleWordWidget(word,true);
int y = answerWords.indexOf(word);
answerWidgetList.removeAt(y); //Problem 2
answerWords.remove(word);
});
},
),
);
}
I added a visibility detector which is triggered with a bool value. I used the shuffleWords list to get the index of the widget and hid it by changing the bool to false. Similarly answerWords can be used for removing widgets from answerWidgetList. As the widget from answerWidgetList is removed therefore we don't need visibility widget there.
Or if shuffleWordWidget is replaced with this then Visibility() is not needed and the image in the question is obtained.
shuffleWordWidget(String word, bool visible) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: InkWell(
child: Container(
color: visible == true ? Colors.white54 : Colors.grey,
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Text(
word,
style: TextStyle(
fontSize: 12,
color: visible == true ? Colors.black : Colors.transparent),
),
),
),
onTap: () {
if (visible == true) {
setState(() {
//shuffleWidgetList.remove(shuffleWordWidget(word));
int i = shuffleWords.indexOf(word);
shuffleWidgetList[i] = shuffleWordWidget(word, false);
//not necessary remove. Look at Problem 1.a
answerWidgetList.add(answerWordWidget(word));
answerWords.add(word);
});
}
},
),
);
}

Flutter Firebase where query arrayContains in List [duplicate]

This question already has answers here:
Firestore search array contains for multiple values
(6 answers)
Closed 1 year ago.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class SearchPage extends StatefulWidget {
#override
_SearchPageState createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> with TickerProviderStateMixin {
final searchController = TextEditingController();
final _firestore = FirebaseFirestore.instance;
static const defaultSearch = "";
String search = defaultSearch ;
void dispose() {
searchController.dispose();
super.dispose();
}
#override
void initState() {
super.initState();
searchController.addListener(searchChanged);
}
searchChanged() {
setState(() {
search = searchController.text;
});
}
#override
Widget build(BuildContext context) {
var tarifRef = _firestore
.collection("vehicles")
.where("models", arrayContains: search);
return Scaffold(
body: Container(
child: ListView(
children: <Widget>[
Expanded(
child: Container(
height: MediaQuery.of(context).size.height * 0.08,
margin: EdgeInsets.only(top: 25),
child: Text(
"Vehicles",
style: TextStyle(
fontSize: 20, fontFamily: "Quando", color: Colors.indigo),
),
),
),
Expanded(
child: Container(
margin: EdgeInsets.only(
top: 10.0, bottom: 10.0, right: 30, left: 30),
child: TextField(
keyboardType: TextInputType.text,
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
),
controller: searchController,
),
),
),
Expanded(
child: Container(
height: MediaQuery.of(context).size.height * 0.50,
child: StreamBuilder<QuerySnapshot>(
stream: tarifRef.snapshots(),
builder: (BuildContext context, AsyncSnapshot asyncsnapshot) {
if (asyncsnapshot.hasError) {
return Center(
child: Text("Error"),
);
} else {
if (asyncsnapshot.hasData) {
List<DocumentSnapshot> listOfDocumentSnapshot =
asyncsnapshot.data.docs;
return Flexible(
child: ListView.builder(
itemCount: listOfDocumentSnapshot.length,
itemBuilder: (context, index) {
return Card(
color: Colors.indigo,
child: ListTile(
title: Text(
"${listOfDocumentSnapshot[index]["name"]}",
style: TextStyle(
fontSize: 20,
color: Colors.white,
),
),
subtitle: Text(
"${listOfDocumentSnapshot[index]["models"]}",
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
),
trailing: IconButton(
icon: Icon(
Icons.delete,
color: Colors.white,
),
onPressed: () async {
await listOfDocumentSnapshot[index]
.reference
.delete();
},
),
),
);
},
),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}
},
),
),
),
],
),
),
);
}
}
This my all code and also I have database in Firebase like this;
vehicles: [
{
"name": "Vehicle1",
"models": ["bus", "plane", "motorcycle"]
},
{
"name": "Vehicle2",
"models": ["motorcycle", "sporcar", "plane"]
},
{
"name": "Vehicle3",
"models": ["motorcycle", "plane", "bus"]
}
]
In this example I can take one input from user, I can query and display which list includes this data but I want to query more than one data is in lists or not.
For example in this code if user input bus, program display Vehicle1 list but I want user can input more than one data such as plane and motorcycle. And so when the user input the plane and motorcycle, I want it to be displayed the list of Vehicle 2 and Vehicle 3.
I try a lot of different ways but I can't found proparly solution to this problem.
I think you're looking for arrayContainsAny here:
var tarifRef = _firestore
.collection("vehicles")
.where("models", arrayContainsAny: ["bus", "plane"]);
This query will return documents whose models array contains either "bus", "plane" or both.
Also see the FlutterFire documentation for Query.where.

onEnd in CountdownTimer with bool

I tried to set a bool to true when my CountdownTimer() from the package flutter_countdown_timer: 1.5.0 is finished. But it changes all my CountdownTimer from my ListView. I think I must to use a List but I don't know how to use that. I am a beginner, be indulgent please. This is my code :
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_countdown_timer/flutter_countdown_timer.dart';
import './data.dart';
import './favorite_screen.dart';
import './home_list.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class Item {
final String title;
final String description;
final String imageURL;
final int countdown;
final int id;
final bool boole;
Item({
this.title,
this.description,
this.imageURL,
this.countdown,
this.id,
this.boole,
});
}
class _HomeState extends State<Home> {
List<Item> savedItems = new List<Item>();
//List<bool> addFavorite = List<bool>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
actions: [
IconButton(
icon: Icon(Icons.favorite_border),
onPressed: () => pushToFavorite(context))
],
),
body: ListView.builder(
itemCount: itemData.length,
itemBuilder: (context, index) {
bool isSaved = savedItems.contains(itemData[index]);
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
TextObject(
itemData[index].title, itemData[index].description),
TimerWidget(itemData[index].countdown),
Spacer(),
Container(
padding: const EdgeInsets.only(left: 8.0, top: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(7.0),
child: GestureDetector(
child: Icon(
isSaved
? Icons.favorite
: Icons.favorite_border,
color: isSaved ? Colors.red : null,
size: 32,
),
onTap: () {
setState(() {
if (isSaved) {
savedItems.remove(itemData[index]);
isSaved = false;
} else {
savedItems.add(itemData[index]);
isSaved = true;
}
});
},
),
),
])),
],
),
)
],
);
}));
}
Future pushToFavorite(BuildContext context) {
return Navigator.of(context).push(MaterialPageRoute(
builder: (context) => Favorite(favoriteItem: savedItems)));
}
}
timerWidget.dart
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_countdown_timer/flutter_countdown_timer.dart';
class TextObject extends StatelessWidget {
final String title;
final String description;
const TextObject(this.title, this.description);
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Spacer(),
//Titre
Container(
padding: const EdgeInsets.only(top: 15.0),
child: FittedBox(
child: Text(
title,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
),
//Decription
Container(
padding: const EdgeInsets.only(left: 10.0, top: 15.0),
child: Text(
description,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.normal),
),
),
Spacer(),
],
);
}
}
class TimerWidget extends StatefulWidget {
final int date;
const TimerWidget(
this.date,
);
#override
_TimerWidgetState createState() => _TimerWidgetState();
}
class _TimerWidgetState extends State<TimerWidget> {
bool finished = false;
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.only(top: 10.0),
child: finished == false ? CountdownTimer(
daysSymbol: new Text("j "),
hoursSymbol: new Text(" : "),
minSymbol: new Text(" : "),
secSymbol: new Text(""),
endTime: widget.date,
textStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.black,
),
onEnd: () {
setState(() {
finished = true;
});
},
) : Text('It is finished'));
}
}
Thanks !

How can i save my data in list that it can be used in entire lifecycle of application in flutter?

In my flutter application i am using mvp with clean architecture and my server return 5 items when it hit api, and when i scroll more it again hit api and get next 5 items. And i am saving these items in a list so that i can not call api again and again for already fetched items and this list was used in listview.builder. I had used bottom navigation bar and when i move from product items tab to any other tab and from there came to products items it again fetch products from api and thats not what i want. I want to show that list which was already fetched by that list in products list tab whenever i return back.
Video of my problem.
import 'dart:async';
import 'package:bakery_application/Bloc/TECartBloc.dart';
import 'package:bakery_application/Singleton/CartManager.dart';
import 'package:bakery_application/data/dataSource/product/remote/ProductRemoteDataSource.dart';
import 'package:bakery_application/data/model/responseDTO/ProductResponseDTO.dart';
import 'package:bakery_application/data/model/responseDTO/models/products.dart';
import 'package:bakery_application/data/model/responseDTO/models/productsList.dart';
import 'package:bakery_application/domain/repository/product/ProductRepo.dart';
import 'package:bakery_application/ui/productdetailscreeen/ProductDetailScreen.dart';
import 'package:bakery_application/ui/productscreen/IProductPresenter.dart';
import 'package:bakery_application/ui/productscreen/IProductView.dart';
import 'package:connectivity/connectivity.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:bakery_application/localmodels/ProductModel.dart';
import 'package:bakery_application/widgets/TEProductIncrementor.dart';
import 'package:modal_progress_hud/modal_progress_hud.dart';
import 'package:provider/provider.dart';
import 'ProductPresenter.dart';
class ProductScreen extends StatefulWidget {
Key key;
List<Products> myList = new List<Products>();
int _pageNumber = 1;
ProductScreen({
this.key,
}) : super(key: key);
#override
_ProductScreenState createState() => _ProductScreenState();
}
class _ProductScreenState extends State<ProductScreen> implements IProductView {
_ProductScreenState() {
productPresenter = ProductPresenter(
this,
ProductRepo(
ProductRemoteDataSource(),
),
);
}
bool circularindicator = false;
Color circularColor;
double circularOpacity;
IProductPresenter productPresenter;
AsyncSnapshot snapshotList;
var _connectionStatus = 'Unknown';
Connectivity connectivity;
StreamSubscription<ConnectivityResult> subscription;
ScrollController _scrollController = ScrollController();
bool cupertinoProgress;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.orange,
automaticallyImplyLeading: false,
title: Text(
'Product',
style: TextStyle(color: Colors.white),
),
),
body:
StreamBuilder(
stream: productPresenter.getProducts,
builder: (context, AsyncSnapshot<ProductsList> snapshot) {
if (snapshot.hasData) {
return productListView(widget.myList);
} else if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
return Center(
child: Container(
child: CircularProgressIndicator(),
),
);
},
),
);
}
Future<Null> refreshList() async {
await Future.delayed(Duration(seconds: 2));
setState(() {});
return null;
}
Widget productListView(List snapshot) {
return RefreshIndicator(
onRefresh: refreshList,
child:
ListView.builder(
key: widget.key,
controller: _scrollController,
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemBuilder: (BuildContext context, int index) {
if(index+1 == widget.myList.length) {
CupertinoActivityIndicator();
}
var plist = widget.myList[index];
return GestureDetector(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailScreen(
product: plist,
),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
width: 120,
height: 130,
child: Image(
image: NetworkImage(plist.image),
),
),
Container(
child: Flexible(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
plist.name,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
Text(
plist.description,
softWrap: true,
textAlign: TextAlign.left,
),
SizedBox(
height: 7,
),
Row(
children: <Widget>[
Text(plist.price.toString(),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
)),
Spacer(),
Text(plist.brand),
],
),
TEProductIncrementor(
product: plist,
),
//TODO reverse it alse
],
),
),
),
),
],
),
Row(
children: <Widget>[
Expanded(
child: Divider(
color: Colors.grey.shade300,
),
),
],
)
],
),
),
);
},
itemCount: widget.myList.length,
),
);
}
productlist(BuildContext context, int index
}
#override
hideProgress(ProductsList response) {
setState(() {
// Here you can write your code for open new view
cupertinoProgress=false;
});
for (var c in response.products) {
widget.myList.add(c);
}
}
#override
showError(String error) {
// TODO: implement showError
return null;
}
#override
showProgress() {
print('Successful');
setState(() {
cupertinoProgress=true;
});
}
#override
void initState() {
super.initState();
connectivity = new Connectivity();
print('Init state called');
print('Init state called');
print('Init state called');
subscription =
connectivity.onConnectivityChanged.listen((ConnectivityResult result) {
_connectionStatus = result.toString();
print(_connectionStatus);
if (result == ConnectivityResult.wifi ||
result == ConnectivityResult.mobile) {
widget.myList.isEmpty
? productPresenter.fetchProducts(widget._pageNumber.toString())
:
widget._pageNumber = widget._pageNumber + 1;
productPresenter.fetchProducts(widget._pageNumber.toString(),);
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
widget._pageNumber = widget._pageNumber + 1;
print(widget._pageNumber);
productPresenter.fetchProducts(widget._pageNumber.toString(),);
}
});
} else {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Center(
child: Container(
child: Icon(
Icons.add_alert,
color: Colors.orange,
size: 20,
),
),
),
content: Text('Check your internet'),
actions: <Widget>[
new FlatButton(
child: new Text('Ok'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
});
}
#override
void dispose() {
subscription.cancel();
super.dispose();
}
getMoreData() async {
}
}
The main problem you're facing is that each time the user scrolls of the tab you're in and returns to it, the app re-fetches the data again.
You can use IndexedStack to make the data only loads once, for that you can edit your code to be this way.
class BaseScreenState extends State<BaseScreen> {
List<Widget> _pages = [Page1(), Page2(), Page3()];
#override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: currentIndex,
children: _pages,
),
);
}
}

change containerColor from a list

I have previously asked a question regarding lists in Flutter. I got good help but new problems arose within the same list. Since this new probelm is of other character then the initial question I made this new question.
I have a list (the code below is simplified from my working-code to make my problem clearer) of blue colored containers.
When the user types in 5 and press the 'submit'-button the color of the first container should change to green (if not 5 the button should turn red).
The second time the user press the 'submit'-button the second container should change color. And so on...
The problem I'm facing is that I can't get my increment to the list to work.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'listing 4',
theme: ThemeData(primarySwatch: Colors.blue),
home: FirstScreen(),
);
}
}
class FirstScreen extends StatefulWidget {
#override
_FirstScreenState createState() => _FirstScreenState();
}
class _FirstScreenState extends State<FirstScreen> {
int sum = 5;
String userAnswer;
String correction = "";
var _controller = new TextEditingController();
int _counter = 1;
List<Color> colors = [Colors.blue, Colors.blue, Colors.blue];
submitPressed(int index) {
if (userAnswer == sum.toString()) {
setState(() {
correction = sum.toString();
colors[index] = Colors.green;
});
} else {
setState(() {
correction = sum.toString();
colors[index] = Colors.red;
});
}
}
Widget myTextField() {
return Container(
width: 50.0,
child: TextField(
controller: _controller,
textAlign: TextAlign.center,
autofocus: true,
keyboardType: TextInputType.number,
onChanged: (val) {
userAnswer = val;
},
),
);
}
Widget myListBuilder() {
return Container(
height: 50.0,
width: 300.0,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 3,
itemBuilder: buildContainer,
),
),
);
}
Widget buildContainer(BuildContext context, int index) {
return Container(
child: Padding(
padding: const EdgeInsets.only(top: 10.0),
child: Container(
height: 20.0,
width: 15.0,
decoration: BoxDecoration(
color: colors[index], //this is the important line
borderRadius: BorderRadius.all(Radius.circular(8.0))),
),
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Listing 4'),
),
body: Container(
child: Center(
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 10.0),
child: Text('Correct answer is 5',
style: TextStyle(fontSize: 20.0)),
),
myTextField(),
RaisedButton(
child: Text('Submit'),
onPressed: () {
setState(() {
submitPressed(0); //This will naturally only give index 0
});
},
),
myListBuilder(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
buildContainer(context, 0),
buildContainer(context, 1),
buildContainer(context, 2)
],
),
RaisedButton(
child: Text('Next'),
onPressed: () {
_counter++;
_controller.clear();
myTextField();
},
),
Text('This should be container no: $_counter'),
],
),
),
),
);
}
}
I can't figure out why you have this
submitPressed(0);
This code works:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'listing 4',
theme: ThemeData(primarySwatch: Colors.blue),
home: FirstScreen(),
);
}
}
class FirstScreen extends StatefulWidget {
#override
_FirstScreenState createState() => _FirstScreenState();
}
class _FirstScreenState extends State<FirstScreen> {
int sum = 5;
String userAnswer;
String correction = "";
var _controller = new TextEditingController();
int _counter = 0;
List<Color> colors = [Colors.blue, Colors.blue, Colors.blue];
submitPressed(int index) {
if (userAnswer == sum.toString()) {
setState(() {
correction = sum.toString();
colors[index] = Colors.green;
});
} else {
setState(() {
correction = sum.toString();
colors[index] = Colors.red;
});
}
}
Widget myTextField() {
return Container(
width: 50.0,
child: TextField(
controller: _controller,
textAlign: TextAlign.center,
autofocus: true,
keyboardType: TextInputType.number,
onChanged: (val) {
userAnswer = val;
},
),
);
}
Widget myListBuilder() {
return Container(
height: 50.0,
width: 300.0,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 3,
itemBuilder: buildContainer,
),
),
);
}
Widget buildContainer(BuildContext context, int index) {
return Container(
child: Padding(
padding: const EdgeInsets.only(top: 10.0),
child: Container(
height: 20.0,
width: 15.0,
decoration: BoxDecoration(
color: colors[index], //this is the important line
borderRadius: BorderRadius.all(Radius.circular(8.0))),
),
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Listing 4'),
),
body: Container(
child: Center(
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 10.0),
child: Text('Correct answer is 5',
style: TextStyle(fontSize: 20.0)),
),
myTextField(),
RaisedButton(
child: Text('Submit'),
onPressed: () {
setState(() {
submitPressed(_counter);
});
},
),
myListBuilder(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
buildContainer(context, 0),
buildContainer(context, 1),
buildContainer(context, 2)
],
),
RaisedButton(
child: Text('Next'),
onPressed: () {
setState(() {
_counter++;
});
_controller.clear();
myTextField();
},
),
Text('This should be container no: ${_counter +1}'),
],
),
),
),
);
}
}
I changed the _counter to act like an index and use it as the parameter of the method submitPressed.
I also put the increment in a setState, or you saw the new number only after hitting the Submit button.