How to map list with widgets in flutter - list

I want to have a normal questionare type of thing.
As soon as the first question gets answered it should go to next question.
But there was an error as 'type 'Null' is not a subtype of type 'List' in type cast' when I used .map().
The .map() and spread operator used to function in previous versions of flutter but now they are not.Can anyone please give a solution for this?
this is the following code-main.dart
class MyAppState extends State<MyApp> {
int qindex = 0;
void answeQues() {
setState(() {
qindex = qindex + 1;
});
print(qindex);
}
#override
Widget build(BuildContext context) {
var questions = [
{
'questionText': 'what\'s your favourite color?',
'answers': ['R', 'G', 'B', 'Y']
},
{
'questionText': 'what\'s your favourite animal?',
' answers': ['Rabbit', 'Giraffe', 'Bear', 'Yax']
},
{
'questionText': 'what\'s your favourite sport?',
'answers': ['Rugby', 'Cricket', 'Basketball', 'Football']
},
];
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('My first App'),
),
body: Column(
children: [
Questions(
questions[qindex]['questionText'].toString(),
),
...(questions[qindex]['answers'] as List<String>).map((answer) {
return Answers(answeQues, answer);
}).toList()
// questions[qindex]['answers'].entries.map((answer) {
// return Answers(answeQues, answer);
// }).toList()
],
),
),
);
}
}
this is my answers.dart file
import 'package:flutter/material.dart';
class Answers extends StatelessWidget {
final Function selectHandler;
final String answerText;
Answers(this.selectHandler, this.answerText);
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: EdgeInsets.all(10),
child: RaisedButton(
color: Colors.blue,
child: Text(answerText),
onPressed: () {
selectHandler();
},
),
);
}
}
Error:-
type 'Null' is not a subtype of type 'List' in type cast.

For your second question, you have space at the beginning of the key name (' answers'), that causes the problem:
var questions = [
...
{
'questionText': 'what\'s your favourite animal?',
' answers': ['Rabbit', 'Giraffe', 'Bear', 'Yax']
},
...
];
There is nothing wrong about .map() and spread operators, they are still used in Flutter.

Related

Set text from list to Text Widget flutter

I'm trying to set quotes from list to Text widget but i am facing this problem
The argument type 'List<Iterable>' can't be assigned to the parameter type 'List'.
this is my code
import 'package:flutter/material.dart';
void main() {
runApp(myApp());
}
class myApp extends StatefulWidget {
#override
_myAppState createState() => _myAppState();
}
class _myAppState extends State<myApp> {
List<String> quotesList = [
"The secret of getting ahead is getting started",
"Only the paranoid survive",
"It’s hard to beat a person who never gives up.",
"Never Had luck never needed it"
];
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
centerTitle: true,
backgroundColor: Colors.black38,
title: Text("Quotes"),
),
body: Column(
children: [quotesList.map((e) => Text(e))].toList(),
),
),
);
}
}
You don't need to wrap the list with '[' and ']'
Column(
children: quotesList.map((e) => Text(e)).toList(),
),
And if you want to add more widgets, you can use like this
Column(
children: quotesList.map<Widget>((e) => Text(e)).toList()
..addAll([
Container() //You can use addAll method and add some juicy widgets
]),
),
Here is another (easy) approach.
Add this function to your current class -
List<Widget> getTextWidgets() {
List<Widget> _widgets = [];
for(int i=0;i<quotesList.length;i++) {
_widgets.add(
Text(quotesList[i])
);
}
return _widgets;
}
And simply call it like -
body: Column(
children: getTextWidgets(),
),
Remove [] from the quotesList -
quotesList.map((e) => Text(e)).toList(),
This might fix your issue -
import 'package:flutter/material.dart';
void main() {
runApp(myApp());
}
class myApp extends StatefulWidget {
#override
_myAppState createState() => _myAppState();
}
class _myAppState extends State<myApp> {
List<String> quotesList = [
"The secret of getting ahead is getting started",
"Only the paranoid survive",
"It’s hard to beat a person who never gives up.",
"Never Had luck never needed it"
];
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
centerTitle: true,
backgroundColor: Colors.black38,
title: Text("Quotes"),
),
body: Column(
children: quotesList.map((e) => Text(e)).toList(),
),
),
);
}
}
To answer your question, you need to remove the [ and ], to look like this
quotesList.map((e) => Text(e)).toList()
If you want to add more widgets, you can use the spread operator
Column(
children: <Widget>[
// you can add more widgets here
...quotesList.map((e) => Text(e)),
// you can add more widgets here too
],
)

flutter SingleChildScrollView remove item

I have the following code where I generate a list of items(data is taken from Firebase). I would like to implement a functionality to remove items but I don't know how to access the list and how to remove items:
class _MyOfferState extends State<MyOffer> {
List<Widget> items = [];
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
...
body: SingleChildScrollView(
child: Column(
children: [
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('Offers')
builder: (BuildContext context, snapshot) {
snapshot.data.docs.forEach((element) {
element.get('items').forEach((item) {
String _name = element['name'];
String _category = item['category'];
items.add(offer(name, category, context,...));
});
}
);
}
return new Column(
children: List.unmodifiable(() sync* {
yield* items;
}()),
);
},
),
}
}
This is a dynamic class where I have GestureDetector. The item should be deleted when a user clicks on the it.
dynamic offer(name, category, context,) {
return GestureDetector(
child: Container(
child: Row(
children: [
Text(name),
Text(category),
],
),
),
),
onTap: () {
// remove item should be here
},
);
}
Removing the offer from within itself is not the best practice but you can accomplish it in a number of ways. The first I can think of is to pass a function that removes it when creating the offer like this:
items.add(offer(name, category, context,..., () {
setState(() {
FirebaseFirestore.instance
.collection('Offers')
.doc(element['id'])
.delete();
items.remoev(index);
});
}));
You'll need to create the index beforehand and increase it each time but I don't recommend doing it.
The way I would done do this is change the offer to be:
dynamic offer(name, category, context,) {
return Container(
child: Row(
children: [
Text(name),
Text(category),
],
),
);
}
And when creating the offer wrap it in the GestureDetector like this:
items.add(GestureDetector(
child: offer(name, category, context,...)),
onTap: () {
setState(() {
FirebaseFirestore.instance
.collection('Offers')
.doc(element['id'])
.delete();
items.remoev(index);
});
},
);
You'll have to do the same thing with the index but I consider it a better approach since the child has no power over the parent and can't change its state which is a good practice.
you need to pass index of item and delete by index:
int index = 0;
snapshot.data.docs.forEach((element) {
element.get('items').forEach((item) {
String _name = element['name'];
String _category = item['category'];
items.add(offer(index, name, category, context,...));
index++;
});
Widget offer(int index, string name, string category, BuildContext context,) {
return GestureDetector(
child: Container(
child: Row(
children: [
Text(name),
Text(category),
],
),
),
),
onTap: () {
// remove item should be here
items.removeAt(index);
setState((){});
},
);
}
}
);
}
return new Column(
children: List.unmodifiable(() sync* {
yield* items;
}()),
);
Your list is getting build by Stream data the one you provided to your StreamBuilder, do create new list you need to change Stream value, I suggest to keep FirebaseFirestore.instance.collection('Offers') instance in a stream and modify the stream.
class _MyOfferState extends State<MyOffer> {
List<Widget> items = [];
StreamController _controller = StreamController();
#override
void initState() {
super.initState();
_controller.addStream( FirebaseFirestore.instance
.collection('Offers').snapshots());
}
// dont forgot to close stream
#override
void dispose() {
_controller.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
...
body: SingleChildScrollView(
child: Column(
children: [
StreamBuilder<QuerySnapshot>(
stream: _controller.stream,
builder: (BuildContext context, snapshot) {
snapshot.data.docs.forEach((element) {
element.get('items').forEach((item) {
String _name = element['name'];
String _category = item['category'];
items.add(offer(name, category, context,(){
// remove function is here
snapshot.data.docs.removeWhere((e) => e.id == element.id);
_controller.add(snapshot.data);
});
});
}
);
}
return new Column(
children: List.unmodifiable(() sync* {
yield* items;
}()),
);
},
),
}
}
Also pass onTap to your widget function
dynamic offer(name, category, context, onTap) {
return GestureDetector(
child: Container(
child: Row(
children: [
Text(name),
Text(category),
],
),
),
),
onTap: onTap,
);
}

Quicker way to implement into a list?

Im making a list of radio buttons. But cant figure a way to map them. The current method making one by one which is too much and with 10+ radio buttons this is taking up hundreds of lines of code.
enum SingingCharacter {char1, char2, char3, char4}
class _HomeScreenState extends State<HomeScreen> {
SingingCharacter _character = SingingCharacter.char1;
final List myList= ['One','Two','Thre' ];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: Container(
child: Column(children: <Widget>[
RadioListTile<SingingCharacter>(
title: Text('${myList[0]}'),
value: SingingCharacter.char1,
groupValue: _character,
onChanged: (SingingCharacter value) {
setState(() {
_character = value;
});
},
),
RadioListTile<SingingCharacter>(
title: Text('${myList[1]}'),
value: SingingCharacter.char2,
groupValue: _character,
onChanged: (SingingCharacter value) {
setState(() {
_character = value;
});
},
)
....
]),
));
}
}
Any method to loop through this and show in children?
Thanks
You could change your List to a Map and then use this to map your enum values to Widgets. Like this (disclaimer: Code not tested, but something like this should be possible):
enum SingingCharacter {char1, char2, char3}
class _HomeScreenState extends State<HomeScreen> {
SingingCharacter _character = SingingCharacter.char1;
final Map<SingingCharacter, String> radioMap = {SingingCharacter.char1: 'One', SingingCharacter.char2: 'Two', SingingCharacter.char3: 'Three'};
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: Container(
child: Column(
children: _generateRadioButtons()
),
)
);
}
List<Widget> _generateRadioButtons() {
return SingingCharacter.values.map((char) {
return RadioListTile<SingingCharacter>(
title: Text('${radioMap[char]}'),
value: char
groupValue: _character,
onChanged: (SingingCharacter value) {
setState(() {
_character = value;
});
},
);
}).toList();
}
}
You can take advantage of the spread operator and unwrap directly a for loop on your Column.
final List myList= ['One','Two','Thre' ];
return Column(children: [
for (int i = 0; i < myList.length; i++)
RadioListTile<SingingCharacter>(
title: Text(myList[i]),
value: SingingCharacter.values[i],
groupValue: _character,
onChanged: (SingingCharacter value) {
setState(() {
_character = value;
});
},
),
]);

Search bar with ListView possible in Flutter?

I want to implement a searchbar in my flutter application. I have to go through a listview out of ListTiles. Here I want to check if the title of the listtile contains the letters in the search field. Is this possible with a List?
It does not have to be with the title. It could be something else with what I can identify the Tile. But please, not the index, the user would not know it.
Is a List the right widget or do I have to use something else to implement a search Engine in my Application
Rather than using a 3rd party package, you can use native showSearch() function :
showSearch(context: context, delegate: ListSearchDelegate());
And then a class extending SearchDelegate:
class ListSearchDelegate extends SearchDelegate{
ListSearchDelegate({Key key,}): super() ;
List<String> listItems = <String>['One', 'Two', 'Three', 'Four', 'Five'] ;
#override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
icon: Icon(Icons.clear),
onPressed: () {
query = '';
},
),
];
}
#override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
close(context, null);
},
);
}
#override
Widget buildResults(BuildContext context) {
List<String> subList ;
subList = query != '' ? listItems.where((item) => item.contains(query)).toList() :
listItems ;
return ListView.builder(
itemCount: subList.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(subList[index]),
);
}
);
}
#override
Widget buildSuggestions(BuildContext context) {
return Container();
}
}
Try https://pub.dev/packages/flutter_search_panel
List<SearchItem<int>> data = [
SearchItem(0, 'This'),
SearchItem(1, 'is'),
SearchItem(2, 'a'),
SearchItem(3, 'test'),
SearchItem(4, '.'),
];
FlutterSearchPanel<int>(
padding: EdgeInsets.all(10.0),
selected: 3,
title: 'Demo Search Page',
data: data,
icon: new Icon(Icons.check_circle, color: Colors.white),
color: Colors.blue,
textStyle: new TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 20.0, decorationStyle: TextDecorationStyle.dotted),
onChanged: (int value) {
print(value);
},
),

error: The named parameter 'children' isn't defined, while using a ListView

I am using Firebase realtime database to retrieve information and then present it in a scrollable DataTable.
To make the DataTable scrollable, I wrapped it up in a ListView, as per the comment of this post:
DataTable - make scrollable, set background colour and fix/freeze header row and first column
This is my code:
import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';
import 'cprdata.dart';
import 'dart:convert';
class CprAnalysis extends StatefulWidget {
#override
CPRState createState() => CPRState();
}
class CPRState extends State<CprAnalysis> {
///var cpr = UpdateData.getData();
List<FilterData> acData;
List<FilterData> getData() {
var cpr = <FilterData>[];
DatabaseReference cprData = FirebaseDatabase.instance.reference();
cprData.reference().once().then((DataSnapshot snap) {
var d = snap.value;
final jsonE = json.encode(d);
final jsonResponse = json.decode(jsonE);
MyDataList zz = new MyDataList.fromJson(jsonResponse);
zz.myList.forEach((data) {
cpr.add(FilterData(sYMBOL: data.SYMBOL, fORECAST: data.FORECAST));
}
);
},
);
print(cpr);
return cpr;
}
#override
void initState() {
super.initState();
acData = getData();
}
Widget bodydata() => Expanded(
child: ListView(
///shrinkWrap: true,
padding: const EdgeInsets.all(8.0),
childern: <Widget>[
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
columns: <DataColumn>[
DataColumn(
label: Text("Symbol"),
numeric: false,
),
DataColumn(
label: Text("Forecast"),
numeric: false,
),
],
rows: acData.map((data) =>
DataRow(
cells: [
DataCell(
Text(data.sYMBOL),
showEditIcon: false,
placeholder: false,
),
DataCell(
Text(data.fORECAST),
showEditIcon: false,
placeholder: false,
)
],
),
)
.toList()
),
),
]
),
);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("CPR Analysis"),
),
body: Container(
child: bodydata(),
),
);
}
}
class FilterData {
String sYMBOL, fORECAST;
FilterData({
this.sYMBOL,
this.fORECAST});
}
Expected output: Scrollable DataTable.
Actual output: error: The named parameter 'childern' isn't defined under ListView()
You misspelled children as childern in your code.
Make your ListView code as -
ListView(
children: <Widget> [
//Your remaining code
]
),