Color containers in array flutter - list

Question updated apr 10:
Hi!
I'm still stuck and can't get this to work :(
I'm trying to make an app where the user will answer a total of 3 questions before he's navigated to a result-screen.
To show the progress of the questions there will be 3 colored containers in a row. The row will initally be for example blue but when the user answers correct - the container for that question will turn green, and if answer is incorrect the container will turn red.
I could really use some further help here.
Below I have made the code as simple as I can with different colors just to show the different items in the list.
Right now it works fine with the first question, but then it kind of stops.
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 = "";
List<Color> colors = [Colors.blue, Colors.amber, Colors.pink];
submitPressed(int index) {
if (userAnswer == sum.toString()) {
setState(() {
correction = sum.toString();
colors[index] = Colors.green;
});
} else {
colors[index] = Colors.red;
}
}
Widget myListBuilder() {
return ListView.builder(
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)),
),
Container(
width: 50.0,
child: TextField(
textAlign: TextAlign.center,
autofocus: true,
keyboardType: TextInputType.number,
onChanged: (val) {
userAnswer = val;
},
),
),
RaisedButton(
child: Text('Submit'),
onPressed: () {
submitPressed(0);
},
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
buildContainer(context, 0),
buildContainer(context, 1),
buildContainer(context, 2)
],
),
],
),
),
),
);
}
}

Ok, I'm going to assume a few things within this answer, so change them as necessary. The colours you are going to use are Colors.blue for the default color, Colors.green for correct, and Colors.red for incorrect.
You would first initialise a List of colors, all of which will be blue as that is the default color:
List<Color> colors = [Colors.blue, Colors.blue, Colors.blue ..... Colors.blue]
//You will write Colors.blue ten times as there are 10 boxes.
I'm going to assume that you use a ListView.builder here, as you haven't specified it in your code example. You would build your ListView as such:
//Place this within your widget tree
ListView.builder(
itemBuilder: buildContainer,
itemCount: 10,
);
You will then need to modify your buildContainer method as the itemBuilder parameter requires a method to take context and index and output a widget, therefore:
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))
),
),
)
);
}
This will then create 10 boxes that each have gotten their colour from their position within the list of colors created earlier. Now you just have to change the color when they are finished. Using your code example:
if (userAnswer == widget.sum.toString()) {
setState(() {
correction = widget.sum.toString();
//Here we will instead set the specific color in the array
colors[index] = Colors.green;
});
} else {
correction = widget.sum.toString();
colors[index] = Colors.red;
}
The only thing you need to do is make sure the function when you click next takes a variable which is the index of the questions, i.e. the question number you are on.

Related

Selecting from a dropdown list and updating the same list the same time in flutter

How do I ensure that a user does not select the same security question twice by hiding the initially selected question from appearing in the second dropdown button and vice versa in flutter?. i am making a request to the same api for the questions.
Updated the question with some code snippets. Thanks
Container(
height: 60,
width: double.infinity,
decoration: BoxDecoration(
border: Border.all(color: Colors.black,
width: 1),
borderRadius: BorderRadius.circular(5),
),
child: DropdownButtonHideUnderline(
child: DropdownButton(
hint: Padding(
padding: const EdgeInsets.only(left:
20.0),
child: Text(
"Security Question Two",
style: TextStyle(
color: Colors.black,
fontSize: 16,
letterSpacing: 0.3,
fontWeight: FontWeight.w300),
),
),
itemHeight: 100,
isExpanded: true,
value: dropDownSecurityQuestionTwo,
icon: Padding(
padding: const EdgeInsets.only(right:
10.0),
child:
Icon(Icons.keyboard_arrow_down_outlined),
),
iconEnabledColor: Colors.black,
iconSize: 30,
style: TextStyle(
color: Colors.black,
),
items: questions.map((value) {
return DropdownMenuItem(
value: value['ID'].toString(),
child: Padding(
padding: const EdgeInsets.only(left:
20.0),
child: Text(
value['question'].toString(),
),
),
);
}).toList(),
onChanged: (newValue) async {
setState(() {
dropDownSecurityQuestionTwo =
newValue.toString();
print(dropDownSecurityQuestionTwo);
checkSelectedQuestion();
});
},
),
),
),
void checkSelectedQuestion(){
List newQuestions = [];
for(int i = 0; i<questions.length; i++){
print(questions[i]['ID']);
questions.removeWhere((value) => value['ID'] ==
int.parse(dropDownSecurityQuestionOne!) );
newQuestions.add(questions);}
setState(() {
questions = newQuestions ;
});}
You can add a where filter to the mapping of items to each DropDownButton, depending on the selected value of the other DropDownButton. As a result of setState, the items will be recreated if anything is selected in the other DropDownButton.
Note: This is easy to implement, but not very efficient. Items will be created and filtered every time. It will work perfectly with few items, but if you would like to do something like this with many items, you might need a more efficient approach. For example keep two items lists, and only add / remove what is affected.
Check this code and adopt it to your case:
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(
home: MyPage(),
);
}
}
class MyPage extends StatefulWidget {
const MyPage({Key? key}) : super(key: key);
#override
State<MyPage> createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
String? _selected1;
String? _selected2;
final List<String> _set = ['Alpha', 'Bravo', 'Charlie'];
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Column(children: [
DropdownButton<String>(
value: _selected1,
onChanged: (String? newValue) {
setState(() {
_selected1 = newValue!;
});
},
items: _set
.map((value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
})
.where((e) => _selected2 == null || e.value != _selected2)
.toList()),
DropdownButton<String>(
value: _selected2,
onChanged: (String? newValue) {
setState(() {
_selected2 = newValue!;
});
},
items: _set
.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
})
.where((e) => _selected1 == null || e.value != _selected1)
.toList()),
]),
),
),
);
}
}

How to decrease space of List in Page?

I am trying to insert a list in a row format into an initial page of an app but the UI of it shows too much spacing between. I actually got the UI off a youtube tutorial and was trying to implement its elements. How can I adjust my code accordingly to reduce the spacing as shown below?
Code:
class Firstpage extends StatefulWidget {
#override
_FirstpageState createState() => _FirstpageState();
}
class _FirstpageState extends State<Firstpage> {
int currentPage = 0;
List<Map<String, String>> splashData = [
{
"text": "Welcome to Tokoto, Let’s shop!",
},
{
"text":
"We help people conect with store \naround United State of America",
},
{
"text": "We show the easy way to shop. \nJust stay at home with us",
},
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(height: 80),
Container(
width: 340,
height: 250,
child:
new Image.asset('assets/images/trip.png', fit: BoxFit.fill),
),
Center(
child: Text("App", style: TextStyle(
fontSize: 40,
fontWeight: FontWeight.w500,
color: Color(0xFF9a0e2a)
),),
),
SizedBox(height: 10),
Expanded(
child: PageView.builder(
onPageChanged: (value) {
setState(() {
currentPage = value;
});
},
itemCount: splashData.length,
itemBuilder: (context, index) => intro(
text: splashData[index]['text'],
),
),
),
Expanded(
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
splashData.length,
(index) => buildDot(index: index),
),
),
]
)
),
DefaultButton(
text: "Sign Up",
press: () {
Navigator.push(context, MaterialPageRoute(builder: (context)=>Signup()));
},
),
SizedBox(height: 20),
DefaultButton(
text: "Login",
press: () {
Navigator.push(context, MaterialPageRoute(builder: (context)=>Signup()));
},
),
],
)
)
);
}
AnimatedContainer buildDot({int? index}) {
return AnimatedContainer(
duration: Duration(milliseconds: 200),
margin: EdgeInsets.only(right: 5),
height: 6,
width: currentPage == index ? 20 : 6,
decoration: BoxDecoration(
color: currentPage == index ? Color(0xFFeb1f48) : Color(0xFFD8D8D8),
borderRadius: BorderRadius.circular(3),
),
);
}
}
And just for reference, Class Intro is:
class intro extends StatelessWidget {
const intro({
Key? key,
this.text,
this.image,
}) : super(key: key);
final String? text, image;
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text(
text!,
textAlign: TextAlign.center,
),
],
);
}
}
If your not using SingleChildScrollView and direct to Column used try Spacer() to have balance on the spacing or put flex to adjust the spacing between.
Spacer()
or
Spacer(flex: 1),
sample
Column(
children:[
Spacer(),
Container(
width: 340,
height: 250,
child:
new Image.asset('assets/images/trip.png', fit: BoxFit.fill),
),
Center(
child: Text("App", style: TextStyle(
fontSize: 40,
fontWeight: FontWeight.w500,
color: Color(0xFF9a0e2a)
),),
),
Spacer(),
]),
you can reduce your spacing by adjusting your SizedBox()
if you notice there's SizedBox() without a child on your code, that's cause you have so much space on your screen.
and just FYI, you can use Spacer() or Expanded() to make empty spaces more flexible or dynamic toward the user's screen
but keep in mind that you can't use Spacer() or Expanded() if:
you have parents of scrollable with the same direction of the main axis of the corresponding Column or Row (for example you have SingleChildScrollView(child: Column(...)) then you cannot use Expanded on Column. Because the widget will have infinity main axis size, which will cause infinity divided by flex amount) TLDR: you can't use Expanded & Spacer on infinity main axis size
when using the Column / Row be careful with "maximum main axis size" (actually, Row and Column don't pass maximum main size to their children, that's why you will have OverFlow exception when trying to write very long long Text inside a Row without using SizedBox or Expanded or Container, etc.)

Flutter position Carousel Slider not centered

I am using CarouselSlider in my app and right now it looks like this:
The problem is that I don't want the highlighted/selected widget in the middle but on the right. Like this:
So what I need to do is, moving the whole widget to the right so the right half of the list is out of the view and only the left halt is being displayed. But I have no idea how I can achieve this...
This is my code:
#override
Widget build(BuildContext context) {
return Expanded(
child: Container(
height: 50,
child: CarouselSlider.builder(
itemCount: data.length,
itemBuilder:
(BuildContext context, int itemIndex, int pageViewIndex) =>
_buildListItem(itemIndex),
options: CarouselOptions(
viewportFraction: 0.35,
reverse: true,
enlargeCenterPage: true,
onPageChanged: (index, whatever) {
print(index);
},
enableInfiniteScroll: false,
scrollDirection: Axis.horizontal,
)),
),
);
}
Widget _buildListItem(int index) {
return Container(
color: AppColors.red,
height: 50,
width: 100,
child: Center(
child: Padding(
padding: EdgeInsets.only(top: 8.scaled),
child: Text(
data[index].toString(),
style: AppTextStyles.glossAndBloomH2Regular,
),
),
),
);
}
And this is where I call it:
Positioned(
right: sidePadding,
bottom: 30,
left: sidePadding,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButtonWithExpandedTouchTarget(
onTapped: () {
showModalBottomSheet(
context: context,
backgroundColor: AppColors.transparent,
builder: (BuildContext context) {
return ModalBottomSheetView();
},
);
},
svgPath: 'assets/icons/menue.svg',
),
YearSlider(),
],
),
Every help is appreciated! Let me know if you need anything more

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.

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.