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.
List.dart
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
class List extends StatefulWidget {
#override
ListState createState() {
return new ListState();
}
}
class ListState extends State<List> {
static const double lat = 2.813812, long = 101.503413;
static const String map_api= "API_KEY";
#override
Widget build(BuildContext context) {
//method to launch maps
void launchMap() async{
const url = "https://maps.google.com/maps/search/?api=$map_api&query=$lat,$long";
if (await canLaunch(url)) {
print("Can launch");
void initState(){
super.initState();
canLaunch( "https://maps.google.com/maps/search/?api=$map_api&query=$lat,$long");
}
await launch(url);
} else {
print("Could not launch");
throw 'Could not launch Maps';
}
}
//method to bring out dialog
void makeDialog(){
showDialog(
context: context,
builder: (_) => new SimpleDialog(
contentPadding: EdgeInsets.only(left: 30.0, top: 30.0),
children: <Widget>[
new Text("Address: ",
style: TextStyle(
fontWeight: FontWeight.bold
),
),
new ButtonBar(
children: <Widget>[
new IconButton(
icon: Icon(Icons.close),
onPressed: (){
Navigator.pop(context);
}
)
],
)
],
)
);
}
return new Scaffold(
body: new ListView.builder(
itemBuilder: (context, index) => ExpansionTile(
title: new Text("State ${index+1}"),
children: <Widget>[
new ListTile(
title: new Text("Place 1"),
trailing: new Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new IconButton(
icon: Icon(Icons.info),
onPressed: makeDialog
),
new IconButton(
icon: Icon(Icons.directions),
onPressed: launchMap
)
],
),
),
new Divider(height: 10.0),
new ListTile(
title: new Text("Place 2"),
trailing: new Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new IconButton(
icon: Icon(Icons.info),
onPressed: makeDialog
),
new IconButton(
icon: Icon(Icons.directions),
onPressed: launchMap
)
],
),
)
],
),
itemCount: 5,
),
);
}
}
My project currently involves launching Google Maps from the URL launcher library. But the problem I am currently having is Google Maps will open but it does not open to the latitude and longitude that I have set as the variable.
How do i set the URL for it to launch according to the coordinates? Or is there a better alternative?
Please assist.
You need to pass api=1 for url_launcher, you have to pass the encoded url Uri.encodeFull().
find more details in this sample project.
launchURL() async {
static const String homeLat = "37.3230";
static const String homeLng = "-122.0312";
static final String googleMapslocationUrl = "https://www.google.com/maps/search/?api=1&query=${TextStrings.homeLat},${TextStrings.homeLng}";
final String encodedURl = Uri.encodeFull(googleMapslocationUrl);
if (await canLaunch(encodedURl)) {
await launch(encodedURl);
} else {
print('Could not launch $encodedURl');
throw 'Could not launch $encodedURl';
}
}
You can use a Map Launcher plugin
if (await MapLauncher.isMapAvailable(MapType.google)) {
await MapLauncher.launchMap(
mapType: MapType.google,
coords: Coords(31.233568, 121.505504),
title: "Shanghai Tower",
description: "Asia's tallest building",
);
}
try this method
///
/// for launching map with specific [latitude] and [longitude]
static Future<void> openMap({
required double latitude,
required double longitude,
required String label,
}) async {
final query = '$latitude,$longitude($label)';
final uri = Uri(scheme: 'geo', host: '0,0', queryParameters: {'q': query});
await launch(uri.toString());
}
For launching Google Maps, the format[1] for a simple search should be as followed:
https://www.google.com/maps/search/?api=1&query=ADDRESS_OR_LATLNG_HERE
In your case, instead of having the value of 1 for the api parameter, you are inserting "API_KEY", which is incorrect. The value should always be 1, so this can be hard-coded.
[1] https://developers.google.com/maps/documentation/urls/guide#forming-the-url
I'm new to Flutter,
I want to destruct cards created initially and construct them again as per data provided in API call.
Basically when I tap on button in UI, it should call APIs and based on data from API call, if it is different from the data I already have, I want to destruct cards and construct them again.
How I can achieve this?
The cards will auto update their content when you make the call again, it is like refreshing your data.
I have made a simple example with a single card that shows data from this JSON Where I am calling the API first time in initState and then repeating the call each time I press on the FAB.
I am adding the index variable just to show you the updates (updating my single card with the next item in the list)
Also it is worth noting that I am handling the null or empty values poorly for the sake of time.
Also forget about the UI overflow ¯_(ツ)_/¯
class CardListExample extends StatefulWidget {
#override
_CardListExampleState createState() => new _CardListExampleState();
}
class _CardListExampleState extends State<CardListExample> {
Map cardList = {};
int index = 0;
#override
void initState() {
_getRequests();
super.initState();
}
_getRequests() async {
String url = "https://jsonplaceholder.typicode.com/users";
var httpClinet = createHttpClient();
var response = await httpClinet.get(
url,
);
var data = JSON.decode(response.body);
//print (data);
setState(() {
this.cardList = data[index];
this.index++;
});
print(cardList);
print(cardList["name"]);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
floatingActionButton:
new FloatingActionButton(onPressed: () => _getRequests()),
appBar: new AppBar(
title: new Text("Card List Example"),
),
body: this.cardList != {}
? new ListView(children: <Widget>[
new Card(
child: new Column(
children: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Text(
cardList["name"] ?? '',
style: Theme.of(context).textTheme.display1,
),
new Text(
this.cardList['email'] ?? '',
maxLines: 50,
),
],
),
new Text(cardList["website"] ?? '')
],
),
),
])
: new Center(child: new CircularProgressIndicator()),
);
}
}
Yes, Answer from Aziza works.
Though I used the code as below :
void main() =>
runApp(new MaterialApp(
onGenerateRoute: (RouteSettings settings) {
switch (settings.name) {
case '/about':
return new FromRightToLeft(
builder: (_) => new _aboutPage.About(),
settings: settings,
);
}
},
home : new HomePage(),
theme: new ThemeData(
fontFamily: 'Poppins',
primarySwatch: Colors.blue,
),
));
class HomePage extends StatefulWidget{
#override
HomePageState createState() => new HomePageState();
}
class HomePageState extends State<HomePage>{
List data;
Future<String> getData() async{
var response = await http.get(
Uri.encodeFull(<SOMEURL>),
headers: {
"Accept" : "application/json"
}
);
this.setState((){
data = JSON.decode(response.body);
});
return "Success";
}
#override
void initState() {
// TODO: implement initState
super.initState();
this.getData();
}
#override
Widget build(BuildContext context){
return new Scaffold(
appBar : new AppBar(
title : new Text("ABC API"),
actions: <Widget>[
new IconButton( // action button
icon: new Icon(Icons.cached),
onPressed: () => getData(),
)],
),
drawer: new Drawer(
child: new ListView(
children: <Widget> [
new Container(
height: 120.0,
child: new DrawerHeader(
padding: new EdgeInsets.all(0.0),
decoration: new BoxDecoration(
color: new Color(0xFFECEFF1),
),
child: new Center(
child: new FlutterLogo(
colors: Colors.blueGrey,
size: 54.0,
),
),
),
),
new ListTile(
leading: new Icon(Icons.chat),
title: new Text('Support'),
onTap: () {
Navigator.pop(context);
Navigator.of(context).pushNamed('/support');
}
),
new ListTile(
leading: new Icon(Icons.info),
title: new Text('About'),
onTap: () {
Navigator.pop(context);
Navigator.of(context).pushNamed('/about');
}
),
new Divider(),
new ListTile(
leading: new Icon(Icons.exit_to_app),
title: new Text('Sign Out'),
onTap: () {
Navigator.pop(context);
}
),
],
)
),
body: this.data != null ?
new ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int index){
return new Container(
padding: new EdgeInsets.fromLTRB(8.0,5.0,8.0,0.0),
child: new Card(
child: new Padding(
padding: new EdgeInsets.fromLTRB(10.0,12.0,8.0,0.0),
child: new Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new ListTile(
enabled: data[index]['active'] == '1' ? true : false,
title: new Text(data[index]['header'],
style:Theme.of(context).textTheme.headline,
),
subtitle: new Text("\n" + data[index]['description']),
),
new ButtonTheme.bar(
child: new ButtonBar(
children: <Widget>[
new FlatButton(
child: new Text(data[index]['action1']),
onPressed: data[index]['active'] == '1' ? _launchURL :null,
),
],
),
),
],
),
),
),
);
},
)
:new Center(child: new CircularProgressIndicator()),
);
}
}
_launchURL() async {
const url = 'http://archive.org';
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
class FromRightToLeft<T> extends MaterialPageRoute<T> {
FromRightToLeft({ WidgetBuilder builder, RouteSettings settings })
: super(builder: builder, settings: settings);
#override
Widget buildTransitions(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
if (settings.isInitialRoute)
return child;
return new SlideTransition(
child: new Container(
decoration: new BoxDecoration(
boxShadow: [
new BoxShadow(
color: Colors.black26,
blurRadius: 25.0,
)
]
),
child: child,
),
position: new Tween(
begin: const Offset(1.0, 0.0),
end: const Offset(0.0, 0.0),
)
.animate(
new CurvedAnimation(
parent: animation,
curve: Curves.fastOutSlowIn,
)
),
);
}
#override Duration get transitionDuration => const Duration(milliseconds: 400);
}
The above code includes Navigation drawer, page navigation animation and also answer to the above question.
I'm trying to add a camera view to my widget using this plugin, but I haven't been able to find any code examples on setting up a camera view or taking a picture. I only see an example on retrieving images stored on the device. Any help is appreciated.
Flutter has a camera plugin which allows access to the camera, shows a camera view and allows to take pictures.
Hope this helps!
Flutter has a image picker plugin(image_picker: "^0.4.5") which allows access to the camera and gallery. such as
Future getImage() async {
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
_image = image;
});
}
you can change the source to ImageSource.camera for get image from camera.
Sample code for both Take picture and Pick image from gallery.
Step 1:
Add this to your package's pubspec.yaml file:
dependencies:
image_picker: ^0.6.1+4
Step 2:
Android
Add user permission in AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
iOS
Add two rows to the ios/Runner/Info.plist
<key>NSCameraUsageDescription</key>
<string>Can I use the camera please?</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Need library access to pick images</string>
Step 3:
Now in your Dart code, you can use:
import 'package:image_picker/image_picker.dart';
step 4
Add this code
class ImagePickerData extends StatefulWidget {
List Data;
int ITId;
ImagePickerData({this.Data, this.ITId});
#override
AttachmentState createState() => new AttachmentState();
}
class AttachmentState extends State<ImagePickerData> {
File _image;
#override
Widget build(BuildContext context) {
Future getCamera() async {
var image = await ImagePicker.pickImage(source: ImageSource.camera);
setState(() {
_image = image;
});
}
Future getGallery() async {
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
_image = image;
});
}
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
height: 200.0,
child: Center(
child: _image == null
? Text('No image selected.')
: Image.file(_image),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Container(
margin: EdgeInsets.only(bottom: 10.0),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
RaisedButton(
elevation: 4.0,
onPressed: () {
getGallery();
},
textColor: Colors.white,
padding: const EdgeInsets.all(0.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(80.0)),
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: <Color>[
Color(0xFFf7418c),
Color(0xFFfbab66),
],
),
borderRadius:
BorderRadius.all(Radius.circular(80.0))),
padding: const EdgeInsets.fromLTRB(30, 10, 30, 10),
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Text('Picture From Gallery',
style: TextStyle(fontSize: 15)),
],
))),
RaisedButton(
elevation: 4.0,
onPressed: () {
getCamera();
},
textColor: Colors.white,
padding: const EdgeInsets.all(0.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(80.0)),
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: <Color>[
Color(0xFFf7418c),
Color(0xFFfbab66),
],
),
borderRadius:
BorderRadius.all(Radius.circular(80.0))),
padding: const EdgeInsets.fromLTRB(30, 10, 30, 10),
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Take Picture',
style: TextStyle(fontSize: 15)),
],
)),
),
],
),
),
],
),
),
],
);
}
}
All we need to use image picker lib https://pub.dev/packages/image_picker
File _storedImage;
final imageFile = await ImagePicker.pickImage(
source: ImageSource.camera,
maxWidth: 600,
);
setState(() {
_storedImage = imageFile;
});
The imageFile variable will have File object which we used in showing widget
We can show file object in widget as
Image.file(
_storedImage,
fit: BoxFit.cover,
width: double.infinity,
)
Update:
Image Picker dependency is updated https://pub.dev/packages/image_picker
Now use
ImagePicker picker = ImagePicker();
final pickedFile = await picker.getImage(source: ImageSource.camera);
Starting with version 0.6.7 of the image_picker plugin, the API of the plugin changed slightly to allow for web implementations to exist.
The old methods that returned dart:io File objects were marked as deprecated, and a new set of methods that return PickedFile objects were introduced.
More information - ImagePicker Example
image_picker: ^0.6.7+14
add above dependency in pubspec.yaml
iOS developers – You need to add some key-value pairs in this file /ios/Runner/Info.plist
<key>NSPhotoLibraryUsageDescription</key>
<string>Need to take picture from photo library</string>
<key>NSCameraUsageDescription</key>
<string>Need to take picture using Camera</string>
File _image;
final picker = ImagePicker();
Import these :
import ‘dart:io’;
import ‘package:image_picker/image_picker.dart’;
_getImage(ImageSource imageSource) async
{
PickedFile imageFile = await picker.getImage(source: imageSource);
//if user doesn't take any image, just return.
if (imageFile == null) return;
setState(
() {
//Rebuild UI with the selected image.
_image = File(imageFile.path);
},
);
}
Call _getImage()
ElevatedButton.icon(
onPressed: () => _getImage(ImageSource.gallery),
icon: Icon(Icons.image),
label: Text('gallery'),
),
ElevatedButton.icon(
onPressed: () => _getImage(ImageSource.camera),
icon: Icon(Icons.camera),
label: Text('camera'),
),
I am back again with the issue related to the question already posted by me on stack overflow Error: '_elements.contains(element)': is not true
this issue has been plaguing me but i was unable to reproduce the same issue and now i have somehow tried to repro again and I have posted the code for everyone to figure out what is it that i am doing wrong that's throwing up this assertion error and crashing the app.
I am new to programming and any help would be greatly appreciated. I have trimmed down the code and I am aware of the some of the bugs. But, the only main concern is the Failed assertion: line 3927 pos 14: '_dependents.isEmpty': is not true. and Failed assertion: line 1766 pos 12: '_elements.contains(element)': is not true.
Steps to reproduce.
main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:untitled1/addnewbranch.dart';
void main() {
runApp(
new MaterialApp(
home: new Branches(),
),
);
}
class ProjectModel {
BranchSetUpModelData branchesModel;
ProjectModel(this.branchesModel);
}
class BranchSetUpModelData {
String branchName;
String hostelType;
String area;
String city;
BranchSetUpModelData(this.branchName, this.hostelType, this.area, this.city);
}
DatabaseReference _branchesRef;
List<ProjectModel> projectModel = new List();
Map data = {};
List<String> mapKeys = new List();
DataSnapshot dataSnapshot;
class Branches extends StatefulWidget {
//const BranchView({ Key key }) : super(key: key);
const Branches({Key key}) : super(key: key);
#override
_BranchesState createState() => new _BranchesState();
}
class _BranchesState extends State<Branches> {
String userUid = '';
String text;
int noOfBranches = 0;
int itemCount;
Future<Null> getUserUid() async {
try {
//FirebaseUser user = await FirebaseAuth.instance.currentUser();
//userUid = user.uid;
//print(userUid);
_branchesRef =
FirebaseDatabase.instance.reference().child('data').child('branches');
print('branchesref = $_branchesRef');
if (_branchesRef != null) {
try {
_branchesRef.once().then((DataSnapshot snapShot) {
dataSnapshot = snapShot;
print(snapShot is Map);
print(dataSnapshot.value);
data = dataSnapshot.value;
print(data is Map);
print(data);
data.forEach((key, value) {
mapKeys.add(key);
});
print('no of branches = $noOfBranches');
projectModel.clear();
mapKeys.forEach((value) {
_branchesRef.child(value).once().then((DataSnapshot b) {
data = b.value;
data.keys.forEach((k) {
BranchSetUpModelData x = new BranchSetUpModelData(
b.value['branchDetails']['branchName'],
b.value['branchDetails']['hostelType'],
b.value['branchDetails']['area'],
b.value['branchDetails']['city'],
);
print('details from for each loop');
ProjectModel projectModelData = new ProjectModel(x);
projectModel.add(projectModelData);
});
print('projectmodel length = ${projectModel.length}');
});
});
setState(() {
noOfBranches = mapKeys.length;
itemCount = noOfBranches;
});
print('no of branches = $noOfBranches');
data.keys.forEach((k) {
print('inside this foreach loop');
print(k);
});
});
} catch (Exception) {
showDialog(
context: context,
child: new AlertDialog(
content: new Text(Exception.message.toString())));
}
} else {
print('user does not exist');
}
} catch (Exception) {
print(Exception.toString());
showDialog(
context: context,
child: new AlertDialog(
content: new Text(Exception.toString()),
));
}
}
#override
void initState() {
super.initState();
mapKeys.clear();
FirebaseDatabase.instance.setPersistenceEnabled(true);
FirebaseDatabase.instance.setPersistenceCacheSizeBytes(10000000);
getUserUid();
/*setState((){
noOfBranches = mapKeys.length;
});*/
print('noOfBranches in init state= $noOfBranches');
}
#override
Widget build(BuildContext context) {
print('noof branches inside widget build = $noOfBranches');
//if(noOfBranches!=0) {
return new MaterialApp(
title: 'Branches',
theme: new ThemeData(
primaryColor: const Color(0xFF229E9C),
),
home: new Scaffold(
appBar: new AppBar(
title: const Text('Branches'),
backgroundColor: Colors.teal[300],
),
floatingActionButton: new FloatingActionButton(
heroTag: 'branchesHeroTag',
child: new Icon(Icons.add),
backgroundColor: Colors.teal[300],
onPressed: (() {
Navigator.push(
context,
new MaterialPageRoute(
builder: (_) => new AddNewBranch(),
),
);
}),
tooltip: 'Add Branch',
),
body: new Container(
child: new ListView.builder(
padding: const EdgeInsets.only(
left: 4.0,
right: 4.0,
),
itemCount: itemCount,
itemBuilder: (BuildContext context, int index) {
if (noOfBranches != 0) {
// children: <Widget>[
return new InkWell(
onTap: (() {
/*Navigate here to a different page*/
}),
child: new Card(
child: new Column(
children: <Widget>[
new Container(
//margin: const EdgeInsets.only(top:16.0),
padding: const EdgeInsets.only(top: 16.0),
child: new Row(
children: <Widget>[
new Expanded(
child: new Row(
children: <Widget>[
new Container(
margin: const EdgeInsets.only(
left: 16.0,
right: 8.0,
top: 4.0,
bottom: 4.0),
child: new IconButton(
icon: new Icon(Icons.call),
onPressed: (() {}))),
new Container(
child: new Text(
'80/125',
style: new TextStyle(
fontSize: 18.0,
),
),
),
],
),
),
new Expanded(
child: new Row(
textDirection: TextDirection.rtl,
children: [
new Container(
margin:
const EdgeInsets.only(right: 16.0),
child: new Text(
projectModel[index]
.branchesModel
.hostelType,
style: new TextStyle(
fontSize: 18.0,
),
),
),
],
),
),
],
),
),
new Container(
margin:
const EdgeInsets.fromLTRB(16.0, 8.0, 16.0, 4.0),
child: new Row(children: <Widget>[
new Text(
projectModel[index].branchesModel.branchName,
style: new TextStyle(
fontSize: 24.0,
),
),
]),
),
new Container(
margin: const EdgeInsets.only(
left: 16.0, right: 16.0, bottom: 8.0),
child: new Row(
children: <Widget>[
new Text(projectModel[index].branchesModel.city),
],
),
),
],
),
),
); // InkWell ends here so this has to go into ListView.builder
} else {
itemCount = 1;
return new Center(
child: new Column(
// mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'Setup your Hostel',
style: new TextStyle(
fontSize: 24.0,
color: Colors.grey[400],
fontWeight: FontWeight.bold,
),
),
new Text(
'Please click below + icon to Setup your Hostel',
style: new TextStyle(
fontSize: 16.0,
color: Colors.grey[400],
),
),
],
),
);
}
},
), // ListView.builder ends here.
),
),
);
}
}
globals.dart
library my_app.globals;
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
final FirebaseAuth auth = FirebaseAuth.instance;
final DatabaseReference databaseReference = FirebaseDatabase.instance.reference();
addnewbranch.dart
import 'dart:async';
import 'package:meta/meta.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:untitled1/main.dart';
import './globals.dart' as gl;
class BranchSetupModel {
String branchName;
String hostelType;
String area;
String city;
BranchSetupModel(this.branchName, this.hostelType, this.area, this.city);
}
BranchSetupModel branchSetupModel =
new BranchSetupModel('', 'Hostel Type', '', '');
class AddNewBranch extends StatefulWidget {
const AddNewBranch({Key key}) : super(key: key);
#override
State<StatefulWidget> createState() {
return new _AddNewBranchState();
}
}
class _AddNewBranchState extends State<AddNewBranch> {
String actionsString = 'Next';
String userUid;
TextEditingController _branchNameController = new TextEditingController();
TextEditingController _areaController = new TextEditingController();
TextEditingController _cityController = new TextEditingController();
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
bool _formWasEdited = false;
bool _autovalidate = false;
String dropDownvalue = 'Hostel Type';
//FocusNode _branchManagerNameFocusNode = new FocusNode();
String _validateName(String value) {
_formWasEdited = true;
if (value.isEmpty) return 'Name is required.';
final RegExp nameExp = new RegExp(r'^[A-Za-z ]+$');
if (!nameExp.hasMatch(value))
return 'Please enter only alphabetical characters and spaces.';
return null;
}
Future<Null> getUserUid() async {
try {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
userUid = user.uid;
print(userUid);
} catch (Exception) {
print(Exception.toString());
}
}
#override
void initState() {
super.initState();
getUserUid();
_branchNameController =
new TextEditingController(text: branchSetupModel.branchName);
_areaController = new TextEditingController(text: branchSetupModel.area);
_cityController = new TextEditingController(text: branchSetupModel.city);
dropDownvalue = branchSetupModel.hostelType;
branchSetupModel = new BranchSetupModel('', 'Hostel Type', '', '');
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
theme: new ThemeData(
primarySwatch: Colors.teal,
),
title: 'Branch Setup',
home: new Scaffold(
appBar: new AppBar(
title: const Text('Add New Branch'),
backgroundColor: Colors.teal[300],
leading: new IconButton(
icon: new Icon(Icons.arrow_back),
onPressed: (() {
print('back button clicked');
//to be removed and to include functionality here
Navigator.push(
context,
new MaterialPageRoute(
builder: (_) => new Branches(),
),
);
}),
),
actions: <Widget>[
new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new FlatButton(
child: new Text(
actionsString,
style: new TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
onPressed: () {
print('Save button clicked');
String pushKey = gl.databaseReference
.child('data')
.child('branches')
.push()
.key;
gl.databaseReference
.child('data')
.child('branches')
.child(pushKey)
.child('branchDetails')
.set({
"branchName": branchSetupModel.branchName,
"hostelType": branchSetupModel.hostelType,
"area": branchSetupModel.area,
"city": branchSetupModel.city,
"pushKey": pushKey
});
Navigator.push(
context,
new MaterialPageRoute(
builder: (_) => new Branches(),
),
);
},
splashColor: const Color(0xFF229E9C),
),
],
),
),
],
),
body: new Form(
key: _formKey,
child: new ListView(
children: <Widget>[
new Container(
margin: const EdgeInsets.only(left: 16.0, right: 16.0),
child: new Row(
children: <Widget>[
new Text('Hostel Type'),
],
),
),
new Container(
//height: 56.0,
margin: const EdgeInsets.only(left: 16.0, right: 16.0),
child: new Column(
children: <Widget>[
new DropdownButton<String>(
//key: _dropDownKey,
hint: new Text(dropDownvalue),
items: <String>[
'Mens',
'Womens',
].map(
(String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
},
).toList(),
onChanged: (String value) {
setState(() {
dropDownvalue = value;
branchSetupModel.hostelType = value;
print(branchSetupModel.hostelType);
});
},
),
],
),
),
new Container(
margin: const EdgeInsets.only(left: 16.0, right: 16.0),
child: new Row(
children: <Widget>[
new Expanded(
child: new TextField(
autocorrect: false,
decoration: new InputDecoration(
labelText: 'Branch Name',
),
controller: _branchNameController,
onChanged: (String value) {
branchSetupModel.branchName =
_branchNameController.text;
print(branchSetupModel.branchName);
},
//validator: _validateName,
),
),
],
),
),
new Container(
margin: const EdgeInsets.only(left: 16.0, right: 16.0),
child: new Row(
children: <Widget>[
new Expanded(
child: new TextField(
decoration: new InputDecoration(
labelText: 'Area/Location',
),
controller: _areaController,
onChanged: (String value) {
branchSetupModel.area = value;
print(branchSetupModel.area);
},
//validator: _validateName,
),
),
],
),
),
new Container(
margin: const EdgeInsets.only(
left: 16.0,
right: 16.0,
),
child: new Row(
children: <Widget>[
new Expanded(
child: new TextField(
decoration: new InputDecoration(
labelText: 'City',
),
controller: _cityController,
onChanged: (String value) {
branchSetupModel.city = value;
},
),
),
],
),
),
],
),
),
),
);
}
}
pubspec.yaml
name: untitled1
description: A new Flutter project.
dependencies:
flutter:
sdk: flutter
google_sign_in: ^1.0.1
firebase_analytics: ^0.1.1
firebase_auth: ^0.3.1
firebase_database: ^0.1.2
firebase_storage: ^0.0.7
dev_dependencies:
flutter_test:
sdk: flutter
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
Please integrate the firebase app to run the code before trying to reproduce the error and make sure the read and write access set to 'true' as I have not added any authentication.
As far as I know the error only occurs in the main.dart file.It works fine for a few times and then the errors start coming in. First, it throws up the error RangeError (index): Invalid value: Valid value range is empty: 0 this error is for the ListView.builder() and then the other two assertion errors come up.
Please can someone tell me what I am doing wrong or is there a different way to achieve the same goal without these errors.
output from flutter doctor command.
[√] Flutter (on Microsoft Windows [Version 10.0.15063], locale en-GB, channel alpha)
• Flutter at C:\Users\Prayuta\flutter
• Framework revision e8aa40eddd (3 weeks ago), 2017-10-17 15:42:40 -0700
• Engine revision 7c4142808c
• Tools Dart version 1.25.0-dev.11.0
[√] Android toolchain - develop for Android devices (Android SDK 27.0.0)
• Android SDK at C:\Users\Prayuta\AppData\Local\Android\sdk
• Platform android-27, build-tools 27.0.0
• Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
• Java version OpenJDK Runtime Environment (build 1.8.0_152-release-915-b01)
[√] Android Studio (version 3.0)
• Android Studio at C:\Program Files\Android\Android Studio
• Java version OpenJDK Runtime Environment (build 1.8.0_152-release-915-b01)
[√] IntelliJ IDEA Community Edition (version 2017.2)
• Flutter plugin version 18.4
• Dart plugin version 172.4343.25
[√] Connected devices
• Android SDK built for x86 • emulator-5554 • android-x86 • Android 7.1.1 (API 25) (emulator)
Process finished with exit code 0.
I recently had the same exception when I was trying to navigate to another route from a Stateful Widget. Turns out I had forgotten to add the line super.initState(); at the start of my widget's initState()
Once I change it to this it worked perfectly
#override
void initState() {
super.initState();
....
}
This error happens due to incorrect widget configuration, so in other words it's not an error itself, but a symptom of another error.
For OP
OP has already found a solution so I won't elaborate on it. Fixing the error in your ListView should do the trick.
TL;DR
Try:
Calling super.initState() inside all your StatefulWidgets.initState() overrides.
Making sure all your Widget.keys are unique.
Making sure all your widgets that use a builder function have no errors. These widgets include AnimatedBuilder, LayoutBuilder, and external packages like GetX or Provider that have a builder, and any packages that depend on them (like Bloc). With these builder widgets, the exception usually occurs when you build the same Widget with different instance members based on some passed value. For example:
...
BlocBuilder<MyBloc, MyState>(
builder: (context, state) => Container(
color: state is SomeState ? Colors.blue : Colors.red, // Something along these lines
),
)
// or
BlocBuilder<MyBloc, MyState>(
builder: (context, state) => state is SomeState ? Container(color: Colors.red) : Container(color: Colors.blue),
)
....
More Explanation
First of all, I want to note that while the exception message is a bit misleading, the flutter team had a couple TODOs to make the exception clearer and refer specifically to the faulty widget.
As the message mentions, the error is related to duplicate GlobalKeys within the widget tree. Usually, this doesn't happen due to directly using the same Key on two different widgets (because devs are usually aware of that). Instead, it hides itself behind other issues such as the ones mentioned above. They all produce similar effect, however: They cause errors in the widget's lifecycle state.
Forgetting to call initState, for instance, messes up the widget's lifecycle state update. I'm not exactly sure exactly where this happens, but it seems that the framework creates duplicate element/widget to the same widget with the same GlobalKey if the widget's lifecycle state is tampered with. This needs further investigation and confirmation.
The thing with builder widgets is that if the first widget to be returned from the builder has an exception, the framework will mount it, but for some reason, the lifecycle change will not occur when the second widget to be returned from builder is inserted in the tree, and both the first and the second widgets end up with the same Key.
In both cases, when the framework unmounts currently active widgets, it checks widgets' lifecycle state, determines which ones are inactive, and ignores them. However, because lifecycle states of some widgets are wrong, it might treat an inactive widget as active, compare it to currently active widgets only to find duplicates, and then throw the exception.
If you want to check that yourself, you can read src/widgets/framework.dart file referred to in the error message, starting from BuilderOwner.finalizeTree() function.
This happened to me as I wrapped a ListView.builder with an Expanded without a container surrounding the Expanded directly as the body of my Scaffold.
For anyone who may find this helpful, I recently had this issue too and it was caused by incorrectly setting a number of key parameters in a list of widgets. I was setting the key based on an id:
key: Key(item.id.toString())
However due to some other logic item.id can sometimes be null and because there are a number of these widgets I ended up with multiple widgets with the key: Key("null"). Updating my key to ensure it was unique solved my problem:
key: Key(items.indexOf(item).toString())
In my case i was using a modalBox. So i created another form key and used it with it. Thats how i solved mine
For me I was declaring my form key inside the initState(). I just moved out as a global variable and it work.
This is my initSatate() now:
#override
void initState() {
super.initState();
}
In my case I had define a component with static key, and I used the component 3 times inside a ListView. So Flutter was find a problem with the position of some components, because the Flutter search by key to verify changes. To solve my problem, I put the property key as optional.
Before:
MyTextField(
{Key key = const Key("textField"),
this.label,
After change:
MyTextField(
{Key? key,
this.label,
...
if you are using provider and GetX both then this error occurs some time and app crash.
Remove one of them if both not neccesory
In my case I was popping a route in which I was passing arguments like this to fix another problem (this was more of a dummy argument), once I removed the arguments, the problem went away
onPressed: () {
Navigator.of(context)
.pushNamed(EditProductScreen.routeName, arguments: {''});
}),
In the above remove 'arguments : {''}
In my case, the issue was a form i was using in two different screens, while declaring its key before the widget itself, statically. Moving the key declaration in the state solved the issue.
One of my colleague actually changed the way I've built my ListView.builder and I am not seeing the error now. I'll keep an eye on this and post any new ways to answer this question or any errors creeping up again.
I only changed the main.dart file which I have posted for reference.
main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:untitled1/addnewbranch.dart';
void main() {
runApp(
new MaterialApp(
home: new Branches(),
),
);
}
class ProjectModel {
BranchSetUpModelData branchesModel;
ProjectModel(this.branchesModel);
}
class BranchSetUpModelData {
String branchName;
String hostelType;
String area;
String city;
BranchSetUpModelData(this.branchName, this.hostelType, this.area, this.city);
}
DatabaseReference _branchesRef;
List<ProjectModel> projectModel = new List();
Map data = {};
List<String> mapKeys = new List();
DataSnapshot dataSnapshot;
class Branches extends StatefulWidget {
//const BranchView({ Key key }) : super(key: key);
const Branches({Key key}) : super(key: key);
#override
_BranchesState createState() => new _BranchesState();
}
class _BranchesState extends State<Branches> {
String userUid = '';
String text;
Future<Null> getUserUid() async {
try {
//FirebaseUser user = await FirebaseAuth.instance.currentUser();
//userUid = user.uid;
//print(userUid);
_branchesRef =
FirebaseDatabase.instance.reference().child('data').child('branches');
print('branchesref = $_branchesRef');
if (_branchesRef != null) {
try {
_branchesRef.once().then((DataSnapshot snapShot) {
dataSnapshot = snapShot;
print(snapShot is Map);
print(dataSnapshot.value);
data = dataSnapshot.value;
print(data is Map);
print(data);
data.forEach((key, value) {
mapKeys.add(key);
});
projectModel.clear();
mapKeys.forEach((value) {
_branchesRef.child(value).once().then((DataSnapshot b) {
data = b.value;
setState(() {
data.keys.forEach((k) {
BranchSetUpModelData x = new BranchSetUpModelData(
b.value['branchDetails']['branchName'],
b.value['branchDetails']['hostelType'],
b.value['branchDetails']['area'],
b.value['branchDetails']['city'],
);
print('details from for each loop');
ProjectModel projectModelData = new ProjectModel(x);
projectModel.add(projectModelData);
});
});
print('projectmodel length = ${projectModel.length}');
});
});
data.keys.forEach((k) {
print('inside this foreach loop');
print(k);
});
});
} catch (Exception) {
showDialog(
context: context,
child: new AlertDialog(
content: new Text(Exception.message.toString())));
}
} else {
print('user does not exist');
}
} catch (Exception) {
print(Exception.toString());
showDialog(
context: context,
child: new AlertDialog(
content: new Text(Exception.toString()),
));
}
}
#override
void initState() {
super.initState();
mapKeys.clear();
FirebaseDatabase.instance.setPersistenceEnabled(true);
FirebaseDatabase.instance.setPersistenceCacheSizeBytes(10000000);
getUserUid();
/*setState((){
noOfBranches = mapKeys.length;
});*/
}
#override
Widget build(BuildContext context) {
//if(noOfBranches!=0) {
return new MaterialApp(
title: 'Branches',
theme: new ThemeData(
primaryColor: const Color(0xFF229E9C),
),
home: new Scaffold(
appBar: new AppBar(
title: const Text('Branches'),
backgroundColor: Colors.teal[300],
),
floatingActionButton: new FloatingActionButton(
heroTag: 'branchesHeroTag',
child: new Icon(Icons.add),
backgroundColor: Colors.teal[300],
onPressed: (() {
Navigator.push(
context,
new MaterialPageRoute(
builder: (_) => new AddNewBranch(),
),
);
}),
tooltip: 'Add Branch',
),
body: (projectModel.length == 0)
? new Center(
child: new Column(
// mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'Setup your Hostel',
style: new TextStyle(
fontSize: 24.0,
color: Colors.grey[400],
fontWeight: FontWeight.bold,
),
),
new Text(
'Please click below + icon to Setup your Hostel',
style: new TextStyle(
fontSize: 16.0,
color: Colors.grey[400],
),
),
],
),
)
: new Container(
child: new ListView.builder(
padding: const EdgeInsets.only(
left: 4.0,
right: 4.0,
),
itemCount: projectModel.length,
itemBuilder: (BuildContext context, int index) {
// children: <Widget>[
return new InkWell(
onTap: (() {
/*Navigate here to a different page*/
}),
child: new Card(
child: new Column(
children: <Widget>[
new Container(
//margin: const EdgeInsets.only(top:16.0),
padding: const EdgeInsets.only(top: 16.0),
child: new Row(
children: <Widget>[
new Expanded(
child: new Row(
children: <Widget>[
new Container(
margin: const EdgeInsets.only(
left: 16.0,
right: 8.0,
top: 4.0,
bottom: 4.0),
child: new IconButton(
icon: new Icon(Icons.call),
onPressed: (() {}))),
new Container(
child: new Text(
'80/125',
style: new TextStyle(
fontSize: 18.0,
),
),
),
],
),
),
new Expanded(
child: new Row(
textDirection: TextDirection.rtl,
children: [
new Container(
margin: const EdgeInsets.only(
right: 16.0),
child: new Text(
projectModel[index]
.branchesModel
.hostelType,
style: new TextStyle(
fontSize: 18.0,
),
),
),
],
),
),
],
),
),
new Container(
margin: const EdgeInsets.fromLTRB(
16.0, 8.0, 16.0, 4.0),
child: new Row(children: <Widget>[
new Text(
projectModel[index].branchesModel.branchName,
style: new TextStyle(
fontSize: 24.0,
),
),
]),
),
new Container(
margin: const EdgeInsets.only(
left: 16.0, right: 16.0, bottom: 8.0),
child: new Row(
children: <Widget>[
new Text(
projectModel[index].branchesModel.city),
],
),
),
],
),
),
); // InkWell ends here so this has to go into ListView.builder
},
), // ListView.builder ends here.
),
),
);
}
}
I hope this will help everyone who is struggling with the same use case to solve their problem,
Many Thanks.
Mahi.