I'm trying to create a card with a text within a container but I would like to show only a part of the text and when the user click on "show more", show the rest. I saw a Widget to construct text like this here, but I need expand the card container either and I don't know how to do that because I need to know how many lines the text have to expand with the correctly size. Exists a way to calculate the size according the number of lines or characters?
I tried to create the card as follows, where the DescriptionText is the Widget on the link and specify a minHeight in the Container in the hope of expanding the container along with the text but did not work.
Widget _showAnswerCard(Answer answer, User user) {
return Card(
elevation: 3.0,
color: Theme.of(context).backgroundColor,
child: Container(
constraints: BoxConstraints(minHeight: 90),
padding: EdgeInsets.all(10.0),
child: Flex(
direction: Axis.horizontal,
children: <Widget>[
Expanded(flex: 1, child: _showUserAvatar(answer)),
Expanded(flex: 3, child: _showAnswerDetails(answer, user)),
],
),
));
}
Widget _showAnswerDetails(Answer answer, User user) {
return Flex(
direction: Axis.vertical,
children: <Widget>[
Expanded(
flex: 3,
child: DescriptionTextWidget(text: answer.content),
),
Expanded(
flex: 1,
child: _showAnswerOptions(),
)
],
);
}
I'll really appreciate if someone could help me with that.
Just use Wrap widget to wrap your Card widget.
Based on your link for suggested answer. I did change to use Wrap widget.
Jus do copy/paste below code and check.
import 'package:flutter/material.dart';
class ProductDetailPage extends StatelessWidget {
final String description =
"Flutter is Google’s mobile UI framework for crafting high-quality native interfaces on iOS and Android in record time. Flutter works with existing code, is used by developers and organizations around the world, and is free and open source.";
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: const Text("Demo App"),
),
body: new Container(
child: new DescriptionTextWidget(text: description),
),
);
}
}
class DescriptionTextWidget extends StatefulWidget {
final String text;
DescriptionTextWidget({#required this.text});
#override
_DescriptionTextWidgetState createState() =>
new _DescriptionTextWidgetState();
}
class _DescriptionTextWidgetState extends State<DescriptionTextWidget> {
bool flag = true;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Wrap(
children: <Widget>[
Card(
margin: EdgeInsets.all(8),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
child: Column(
children: <Widget>[
Container(
child: Text(
widget.text,
overflow: flag ? TextOverflow.ellipsis : null,
style: TextStyle(
fontSize: 15,
),
),
),
InkWell(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
flag ? "show more" : "show less",
style: new TextStyle(color: Colors.blue),
),
],
),
onTap: () {
setState(() {
flag = !flag;
});
},
),
],
)),
),
],
);
}
}
Result:
The solution I can think of is to use two labels, one for displaying only one line of text and one for displaying all the text. When the button is clicked, the two labels are alternately displayed in an animated manner. There is no computer at the moment, it is not convenient to verify, I hope to give you some help in the implementation of the program.
I am building a flutter app with several different files so I can piece them together as needed. One of the separate files builds out a BottomNavigationBar. The other files build out the separate pages that the BottomNavigationBar will link to. Navigation works fine, but every time I change screens, the color of the BottomNavigationBar (which should be highlighting the screen that is currently active) is snapping back to the first tab. I am certain the reason for this is that when I click on the BottomNavigationBar, it appropriately routes me to the next view (and in fact I see the new tab highlighted on the old view before going to the next one) and then once on the new page, BottomNavigationBar is being called again, where I am establishing that it start at its home point (the first tab) until further notice. Here is the code for the BottomNavigationBar file:
import 'package:flutter/material.dart';
import 'profile.dart';
import 'search.dart';
import 'favorites.dart';
import 'featured.dart';
class NavBar extends StatefulWidget {
#override
NavBarState createState() => NavBarState();
}
class NavBarState extends State<NavBar>{
int currentTab = 0;
FeaturedScreen one;
SearchScreen two;
FavoritesScreen three;
ProfileScreen four;
List<Widget> pages;
Widget currentPage;
#override
void initState() {
one = FeaturedScreen();
two = SearchScreen();
three = FavoritesScreen();
four = ProfileScreen();
pages = [one, two, three, four];
super.initState();
}
#override
Widget build(BuildContext context) {
return BottomNavigationBar(
type: BottomNavigationBarType.fixed,
currentIndex: currentTab,
fixedColor: new Color(0xffffffff).withOpacity(0.5),
onTap: (int index) {
setState((){
currentTab = index;
currentPage = pages[index];
});
Navigator.push(
context,
new MaterialPageRoute(builder: (context) => currentPage)
);
},
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: currentTab==0?Icon(
Icons.apps,
color: Color(0xff70E0EF),
size: 35.0,
):Icon(
Icons.apps,
color: Colors.black,
size: 35.0,
),
title: Text(
'Collections',
style: new TextStyle(
color: Colors.white,
fontSize: 0.0,
height: 0.0,
),
),
),
BottomNavigationBarItem(
icon: currentTab==1?Icon(
Icons.search,
color: Color(0xff70E0EF),
size: 35.0,
):Icon(
Icons.search,
color: Colors.black,
size: 35.0,
),
title: Text(
'Search',
style: new TextStyle(
color: Colors.white,
fontSize: 0.0,
height: 0.0,
),
),
),
BottomNavigationBarItem(
icon: currentTab==2?Icon(
Icons.favorite,
color: Color(0xff70E0EF),
size: 35.0,
):Icon(
Icons.favorite,
color: Colors.black,
size: 35.0,
),
title: Text(
'Favorites',
style: new TextStyle(
color: Colors.white,
fontSize: 0.0,
height: 0.0,
),
),
),
BottomNavigationBarItem(
icon: currentTab==3?Icon(
Icons.person,
color: Color(0xff70E0EF),
size: 35.0,
):Icon(
Icons.person,
color: Colors.black,
size: 35.0,
),
title: Text(
'Profile',
style: new TextStyle(
color: Colors.white,
fontSize: 0.0,
height: 0.0,
),
),
),
],
);
}
}
Each of the four items will load a page that, for now, looks more or less exactly like this:
import 'package:flutter/material.dart';
import 'navbar.dart';
class FeaturedScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Featured(),
bottomNavigationBar: NavBar(),
);
}
}
class Featured extends StatefulWidget {
#override
FeaturedState createState() => FeaturedState();
}
class FeaturedState extends State<Featured>{
#override
Widget build(BuildContext context) {
return Scaffold(
body: new Stack(
fit: StackFit.passthrough,
children: [
new Container(
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage('assets/FeaturedBG.png'),
fit: BoxFit.cover
),
),
),
],
),
);
}
}
How can I make it so that BottomNavigationBar activates correctly per active page?
P.S. Sorry if this was a confusing question. Happy to make a gif of what's happening on request.
In the example code you have posted, each page is getting a new BottomNavigationBar. So when you navigate to it, it will always begin at the initial state. To avoid this problem, you should structure your pages so that there is a single bottom navigation bar that they all share, instead of using the router. You will also need to remove the Scaffold and nav bar from each page.
class MyScreens extends StatefulWidget {
#override
MyScreensState createState() => new MyScreensState();
}
class MyScreensState extends State<MyScreens> {
#override
Widget build(BuildContext context) {
return new Scaffold(
body: _pages[currentTab], // <- change the widget here
navBar: new BottomNavigationBar( /* ... */),
);
}
}
For more ideas on how to structure a bottom nav bar, check out the example code in the flutter gallery.
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.