Flutter ListTile under ExpansionTile - dart

I currently have an ExpansionTile that has a ListView.builder which contains a ListTile.
The task I am doing is putting multiple listTiles under a single Expansiontile.
This is the file list.dart
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'dart:async' show Future;
import 'package:flutter/services.dart' show rootBundle;
import 'dart:convert';
import 'package:emas_app/model/accounts_model.dart';
Future<String> _loadAsset() async{
return await rootBundle.loadString('Assets/accounts.json');
}
Future<Accounts> loadAccounts() async{
final response = await _loadAsset();
final jsonResponse = json.decode(response);
Accounts accounts = new Accounts.fromJson(jsonResponse);
return accounts;
}
class ProviderList extends StatefulWidget {
#override
ListState createState() {
return new ListState();
}
}
class ListState extends State<ProviderList> {
#override
Widget build(BuildContext context) {
Widget body = new FutureBuilder<Accounts>(
future: loadAccounts(),
builder: (context, snapshot){
if(snapshot.hasData){
return new ListView.builder(
itemCount: snapshot.data.accountinfo.length,
itemBuilder: (context,index){
return new Card(
child: new Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new ExpansionTile(
title: new Text("Johor"),
children: <Widget>[
new ListTile(
title: new Text("${snapshot.data.accountinfo[index].name}"),
)
]
),
],
),
);
});
}else{
return new Center(
child: new CircularProgressIndicator(),
);
}
});
return new Scaffold(
appBar: new AppBar(title: new Text("Providers")),
body: body
);
}
}
The main problem I am currently facing is the ExpansionTile keeps on repeating for each new ListTile.
Any thoughts on how should I properly place the ExpansionTile?

because you build
Card(
child: new Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new ExpansionTile(
title: new Text("Johor"),
children: <Widget>[
new ListTile(
title: new Text("${snapshot.data.accountinfo[index].name}"),
)
]
),
],
Till = snapshot.data.accountinfo.length
try to create only ListTile to that length, not the whole card widget

Related

Flutter - fill list with firebase collection

I have a list of messages that I want to fill on init with a firebase collection.
import 'package:flutter/material.dart';
import 'package:my_first_flutter_app/chatmessage.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:logging/logging.dart';
final Logger log = new Logger('ChatScreen');
class ChatScreen extends StatefulWidget {
#override
State createState() => new ChatScreenState();
}
class ChatScreenState extends State<ChatScreen> {
final TextEditingController _chatController = new TextEditingController();
final List<ChatMessage> _messages = <ChatMessage>[];
#override
Widget build(BuildContext context) {
return new Column(
children: <Widget>[
new Flexible(
child: ListView.builder(
padding: new EdgeInsets.all(8.0),
reverse: true,
itemBuilder: (_, int index) => _messages[index],
itemCount: _messages.length,
),
),
new Divider(
height: 1.0,
),
new Container(decoration: new BoxDecoration(
color: Theme.of(context).cardColor,
),
child: _chatEnvironment(),)
],
);
}
}
I tried to do this:
#override
Widget build(BuildContext context) {
Firestore.instance
.collection('chats')
.document('ROOM_1')
.collection("messages")
.getDocuments()
.then((snap) {
return new Column(
....
but I need to return a Widget, while this attempt returns a Future.
How I can fill the _messages array with data coming from my firestore collection on the initialization of my chat screen page?
If you just need to display all messages in a ListView from the firestore collection, then maybe you'll love the StreamBuilder widget. You can do something like this:
return new Column(
children: <Widget>[
new Flexible(
child: StreamBuilder(
stream: Firestore.instance.collection('chats').document('ROOM_1').collection('messages').snapshots(),
builder: (context, snapshot){
if (!snapshot.hasData){
return Container(
child: Center(
child: Text("No data")
)
);
}
return ListView.builder(
padding: EdgeInsets.all(8.0),
reverse: true,
itemCount: snapshot.data.documents.length,
itemBuilder: (_, int index) {
return ChatMessage(text: snapshot.data.documents[index]["messageField"]); //I just assumed that your ChatMessage class takes a parameter message text
}
);
}
)
),
new Divider(
height: 1.0,
),
...
Note that in this example, I didn't use the _messages variable.

ListView.builder Not Building Items (nothing is displayed)

When I don't use the ListView.builder constructor in Flutter, the individual item is shown as expected from the JSON API:
On the other hand, when I use ListView.builder, nothing shows up.
Here's the code:
import 'dart:ui';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart'as http;
import "package:flutter/material.dart";
import 'package:flutter/painting.dart';
Map responsee={};
bool _loading = false;
class tag extends StatefulWidget{
Map data={};
tag(this.data);
#override
State<StatefulWidget> createState() {
return tagstate(data);
}
}
class tagstate extends State<tag>{
List influ=[{"username":"tarun"}];
Map data={};
tagstate(this.data);
Future<Null> load()async {
responsee = await getJson1(data["tag"]);
setState(() {
_loading = true;
influ=responsee["influencers"];
new Future.delayed(new Duration(seconds: 5), _login);
});
print('length: ${influ}');
}
Future _login() async{
setState((){
_loading = false;
});
}
#override
void initState() {
load();
super.initState();
}
#override
build(BuildContext context) {
var bodyProgress = new Container(
child: new Stack(
children: <Widget>[
new Container(
alignment: AlignmentDirectional.center,
decoration: new BoxDecoration(
color: Colors.white70,
),
child: new Container(
decoration: new BoxDecoration(
color: Colors.blue[200],
borderRadius: new BorderRadius.circular(10.0)
),
width: 300.0,
height: 200.0,
alignment: AlignmentDirectional.center,
child: new Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Center(
child: new SizedBox(
height: 50.0,
width: 50.0,
child: new CircularProgressIndicator(
value: null,
strokeWidth: 7.0,
),
),
),
new Container(
margin: const EdgeInsets.only(top: 25.0),
child: new Center(
child: new Text(
"loading.. wait...",
style: new TextStyle(
color: Colors.white
),
),
),
),
],
),
),
),
],
),
);
return Scaffold(
appBar: new AppBar(iconTheme: IconThemeData(color: Colors.black),backgroundColor: Colors.white,
title: Text("Stats",style: TextStyle(color: Colors.black,fontWeight: FontWeight.w600),),
),
body: _loading ? bodyProgress : new Column(children: <Widget>[
Flexible(child: ListView.builder(padding: const EdgeInsets.all(14.5),itemCount: influ.length,itemBuilder: (BuildContext context,int pos){
new ListTile(
title: Text(influ[pos]["username"],style: new TextStyle(fontSize: 17.9),),
leading: CircleAvatar(
backgroundColor: Colors.pink,
child: Image.network("${influ[pos]["photo"]}"),
),
);
}),)],),
);
}
}
Future<Map> getJson1(String data) async{
String apiUrl="https://api.ritekit.com/v1/influencers/hashtag/$data?client_id=a59c9bebeb5253f830e09bd9edd102033c8fe014b976";
http.Response response = await http.get(apiUrl);
return json.decode(response.body);
}
No matter how much I try, the error still persists.
The Scaffold loads, but the ListView.builder doesn't.
When I don't use the ListView.builder, the individual item is shown as expected from the JSON API.
Thank you everyone...
I actually forgot to return the Listtile in the Itembuiler Function..
Thanks Again
Future<Null> load()async {
responsee = await getJson1(data["tag"]);
influ=responsee["influencers"];
}
should be
Future<Null> load()async {
responsee = await getJson1(data["tag"]);
setState(() => influ=responsee["influencers"]);
}
await getJson1(data["tag"]); is async and needs to notify Flutter to rebuild when the response arrives.
Because load() is async, it's not sure what "tagstate.build()" does. My suggestion is to do the loading in the parent widget, then when the loading is done, pass influ to the tag widget. E.g.
onPress(() {
final influ = (await getJson1(data["tag"]))['influencers'];
Navigator.of(context).push(
(new MaterialPageRoute(builder: (context) {
return tag(influ: influe);
}));
}
Move List influ = [] into tagState class and use setState as above answer. Everything should work now.
Please refer this. influ was global variable initially because of which even setState will not work. If we want our Stateful widget to react based on some value, it should be its instance variable, not local variable and not global variable.

How can I render only one Widget setState

I have two easy basics example one is Works and second need some of explaining.
So, When I Clik Button in Example one it Works and setStaet rendering the page.
Second Example I using a body function that has setState of Button. When I click button nothing happen even with using setState notes if
the variable inside the parenthesis it does not render.
When I make Variable instance in class it works fine but I need to rendering Only One Button how?
Full code Example One
import 'package:flutter/material.dart';
main() => runApp(MaterialApp(
home: Home(),
));
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
bool istrue = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Works fine'),),
body: Center(child: body()),
);
}
Widget body() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () {
setState(() {
istrue = !istrue;
});
},
child: Text('Click me'),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(istrue ? 'Boo!' : ''),
),
],
);
}
}
Full code Example two
import 'package:flutter/material.dart';
main() => runApp(MaterialApp(
home: Home(),
));
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
bool istrue = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Works fine'),),
body: Center(child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
body(),body(),
],
)),
);
}
Widget body() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () {
setState(() {
istrue = !istrue;
});
},
child: Text('Click me'),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(istrue ? 'Boo!' : ''),
),
],
);
}
}
Thanks as sky.
I am having a bit of trouble understanding your question, but I also understand that English is not your first language. You seem to be asking, "How can I render only one button for example #2?". Please let me know if this is not your question so that I, and others, can help you.
This will only render one button,
import 'package:flutter/material.dart';
main() => runApp(MaterialApp(
home: Home(),
));
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
bool istrue = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Works fine'),),
body: Center(child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () {
setState(() {
istrue = !istrue;
});
},
child: Text('Click me'),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(istrue ? 'Boo!' : ''),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(istrue ? 'Boo!' : ''),
),
],
)),
);
}
}
You are calling a Column widget within another Column widget in the Scaffold widget in your second example. Why would you do that? There are many ways to structure that layout in the flutter API.
You can't have a new Column(new Column(...)); Its redundant.
Edit:
I pulled this from the flutter website at Column class:
The Column widget does not scroll (and in general it is considered an error to have more children in a Column than will fit in the available room). If you have a line of widgets and want them to be able to scroll if there is insufficient room, consider using a ListView.

Flutter Cards - How to Loop a card widgets in Flutter

Main.dart
I want to loop the cards in the flutter.Since in Angular 2 just *ngFor works fine now in same way how can i loop it.I don't found and docs on flutter web.
you will find the output in the screen shot
Please help me to know how to loop cards or any other widgets
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
home:new MyCard()
);
}
}
class MyCard extends StatelessWidget{
#override
Widget build(BuildContext context) {
Widget allcards;
return new Scaffold(
appBar: new AppBar(
title: new Text('My First App'),
backgroundColor:new Color(0xFF673AB7),
),
body: new Container(
child: new Column(
children: <Widget>[
new Card(
child: new Column(
children: <Widget>[
new Image.network('https://i.ytimg.com/vi/fq4N0hgOWzU/maxresdefault.jpg'),
new Padding(
padding: new EdgeInsets.all(7.0),
child: new Row(
children: <Widget>[
new Padding(
padding: new EdgeInsets.all(7.0),
child: new Icon(Icons.thumb_up),
),
new Padding(
padding: new EdgeInsets.all(7.0),
child: new Text('Like',style: new TextStyle(fontSize: 18.0),),
),
new Padding(
padding: new EdgeInsets.all(7.0),
child: new Icon(Icons.comment),
),
new Padding(
padding: new EdgeInsets.all(7.0),
child: new Text('Comments',style: new TextStyle(fontSize: 18.0)),
)
],
)
)
],
),
)
],
),
)
);
}
}`
This is my dart file
screen shot
Just like Angular2 having an iteratable to loop over is what makes any loop works.
So I did some refactoring in your code and added a the list, changed Column with a ListView and here is the result:
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
home:new MyCard()
);
}
}
class MyCard extends StatelessWidget{
List cards = new List.generate(20, (i)=>new CustomCard());
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('My First App'),
backgroundColor:new Color(0xFF673AB7),
),
body: new Container(
child: new ListView(
children: cards,
)
)
);
}
}
class CustomCard extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Card(
child: new Column(
children: <Widget>[
new Image.network('https://i.ytimg.com/vi/fq4N0hgOWzU/maxresdefault.jpg'),
new Padding(
padding: new EdgeInsets.all(7.0),
child: new Row(
children: <Widget>[
new Padding(
padding: new EdgeInsets.all(7.0),
child: new Icon(Icons.thumb_up),
),
new Padding(
padding: new EdgeInsets.all(7.0),
child: new Text('Like',style: new TextStyle(fontSize: 18.0),),
),
new Padding(
padding: new EdgeInsets.all(7.0),
child: new Icon(Icons.comment),
),
new Padding(
padding: new EdgeInsets.all(7.0),
child: new Text('Comments',style: new TextStyle(fontSize: 18.0)),
)
],
)
)
],
),
);
}
}
In case if any one gets error with above solution please add .toList() to
List cards = new List.generate(20, (i)=>new CustomCard()).toList();
In order to avoid this error:
This class (or a class which this class inherits from) is marked as '#immutable', but one or more of its instance fields are not final:
Just put
final
At this line:
List cards = new List.generate(20, (i)=>new CustomCard());
Staying this:
final List cards = new List.generate(20, (i)=>new CustomCard());

flutter loop inside a widget

I'm new to flutter and was wondering if anyone would be able to guide me.
I would like to irritate through a string that I retrieved from a json file. I would like to display each letter separately. Example below.
Output
Complete Word: Hi
Letter pos 0: H
Letter pos 1: I
What I tried so far is adding a for loop below the itemBuilder but can't retrieve the variable inside the card. I tried adding a for loop inside the Widget and it doesn't allow me.
Any input would be greatly appreciated!
import 'dart:convert';
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
home: new MyApp(),
));
}
class MyApp extends StatefulWidget {
#override
MyAppState createState() => new MyAppState();
}
class MyAppState extends State<MyApp> {
List data;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Load local JSON file"),
),
body: new Container(
child: new Center(
// Use future builder and DefaultAssetBundle to load the local JSON file
child: new FutureBuilder(
future: DefaultAssetBundle
.of(context)
.loadString('data_repo/starwars_data.json'),
builder: (context, snapshot) {
// Decode the JSON
var new_data = JSON.decode(snapshot.data.toString());
return new ListView.builder(
// Build the ListView
itemBuilder: (BuildContext context, int index) {
return new Card(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Text("Complete Word: " + new_data[index]['complete_word'])
],
),
);
},
itemCount: new_data == null ? 0 : new_data.length,
);
}),
),
));
}
}
Create a method and within it create a list of widgets. Include the widgets and then return the list.
Example
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: ListMyWidgets()),
List<Widget> ListMyWidgets() {
List<Widget> list = new List();
list.add(new Text("hi"));
list.add(new Text("hi2"));
list.add(new Text("hi3"));
return list;
}

Resources