Stack Overflow Asked by Rogerto on November 27, 2021
I’ve built a web app using Flutter. Built and deployed with no issues a couple of months ago. Have jumped back into the code today without updating any code and now am getting the following error:
Error:Expected a value of type 'List<String>', but got one of type 'List<dynamic>'
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following JSNoSuchMethodError was thrown building NewHomeScreen(dirty, dependencies:
[_EffectiveTickerMode, _InheritedProviderScope<List<ContentModel>>, MediaQuery], state:
_NewHomeScreenState#295f1(tickers: tracking 2 tickers)):
NoSuchMethodError: invalid member on null: 'length'
This is where and how I get data from Firebase:
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
// This widget is the root of your application.
@override
_MyAppState createState() => _MyAppState();
}
@override
void initState() {}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
final linksCollection = Firestore.instance.collection('links');
final contentCollection = Firestore.instance.collection('content');
final contentObjects = contentCollection.snapshots().map((snapshot) {
return snapshot.documents
.map((doc) => ContentModel.fromDocument(doc))
.toList();
});
return MultiProvider(
providers: [
StreamProvider<List<ContentModel>>(
create: (_) => contentObjects,
initialData: [],
catchError: (BuildContext context, e) {
print("Error:$e");
return null;
},
),
Provider<CollectionReference>(create: (_) => linksCollection),
],
child: MaterialApp(
title: 'My App',
debugShowCheckedModeBanner: false,
theme: ThemeData(primarySwatch: Colors.blue, fontFamily: 'IBM_Plex'),
initialRoute: '/',
routes: {'/': (context) => NewHomeScreen()},
),
);
}
}
I then consume this data throughout the app by accessing it using Provider like so:
class NewHomeScreen extends StatefulWidget {
@override
_NewHomeScreenState createState() => _NewHomeScreenState();
}
class _NewHomeScreenState extends State<NewHomeScreen>
with TickerProviderStateMixin {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
final contentObjects = Provider.of<List<ContentModel>>(context);
List<ContentModel> expertList = [];
for (var data in contentObjects) {
if(data.topic == 'expert') {
expertList.add(data);
}
}
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
leading: Container(
child: Padding(
padding: EdgeInsets.only(left: 10.0),
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NewHomeScreen(),
),
);
},
),
)
),
backgroundColor: appBarColor,
expandedHeight: 50.0,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Align(
alignment: Alignment.center,
),
centerTitle: true,
stretchModes: [
StretchMode.blurBackground,
StretchMode.zoomBackground
],
background: Image.network(
'https://www.image.com',
fit: BoxFit.cover,
),
),
actions: <Widget>[
InkResponse(
onTap: () {
Navigator.push(
context,
SlideRightRoute(
page: SearchScreen(),
),
);
},
child: new Padding(
padding: const EdgeInsets.all(12.0),
child: Icon(
Icons.search,
size: 26.0,
color: Colors.white,
),
),
),
],
),
SliverToBoxAdapter(
child: Column(
children: <Widget>[
FadeIn(1.00, Center(child: HeaderWidget())),
FadeIn(2.33, Center(child: HashtagRow())),
SizedBox(
height: 20,
),
SizedBox(height: 50),
FadeIn(
2.66,
SectionContainer(
sectionTitle: "Expertise in focus",
child: Padding(
padding: EdgeInsets.only(top: 13, bottom: 13),
child: Container(
height: 450,
child: ListView.builder(
padding: EdgeInsets.only(left: 50, right: 50),
scrollDirection: Axis.horizontal,
itemCount: expertList.length,
itemBuilder: (ctx, index) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ExpertDetailsScreen(
contentModel: expertList[index],
),
),
);
},
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(
left: 15.0,
right: 15.0,
),
child: Hero(
tag: expertList[index].title.toString(),
child: Align(
alignment: Alignment.centerLeft,
child: CircleAvatar(
radius: 150.0,
backgroundImage: NetworkImage(
expertList[index].imglink),
backgroundColor: Colors.transparent,
),
),
),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
),
child: Padding(
padding: const EdgeInsets.all(10),
child: Center(
child: Text(
expertList[index].tags[1],
textAlign: TextAlign.center,
style: forumNameTextStyleTwo,
),
),
),
),
SizedBox(height: 3),
Text(
expertList[index].title,
textAlign: TextAlign.center,
style: labelTextStyle,
),
],
),
);
},
),
),
),
),
),
SizedBox(height: 50)
],
),
)
],
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
Navigator.push(
context,
ScaleRoute(
page: AddResource(),
),
);
},
label: Text('Suggest a resource'),
icon: Icon(Icons.add),
backgroundColor: myColor,
),
);
}
void htmlOpenLink(String s) {
html.window.open(s, '_blank');
}
}
class SlideRightRoute extends PageRouteBuilder {
final Widget page;
SlideRightRoute({this.page})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) =>
page,
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) =>
SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1, 0),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: animation,
curve: Curves.fastOutSlowIn,
),
),
child: child,
),
);
}
class ScaleRoute extends PageRouteBuilder {
final Widget page;
ScaleRoute({this.page})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) =>
page,
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) =>
ScaleTransition(
scale: Tween<double>(
begin: 0.0,
end: 1.0,
).animate(
CurvedAnimation(
parent: animation,
curve: Curves.fastOutSlowIn,
),
),
child: child,
),
);
}
class MyCustomClipper extends CustomClipper<Path> {
final double distanceFromWall = 12;
final double controlPointDistanceFromWall = 2;
@override
Path getClip(Size size) {
final double height = size.height;
final double halfHeight = size.height * 0.5;
final double width = size.width;
Path clippedPath = Path();
clippedPath.moveTo(0, halfHeight);
clippedPath.lineTo(0, height - distanceFromWall);
clippedPath.quadraticBezierTo(0 + controlPointDistanceFromWall,
height - controlPointDistanceFromWall, 0 + distanceFromWall, height);
clippedPath.lineTo(width, height);
clippedPath.lineTo(width, 0 + 30.0);
clippedPath.quadraticBezierTo(width - 5, 0 + 5.0, width - 35, 0 + 15.0);
clippedPath.close();
return clippedPath;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return true;
}
}
class CustomShapeBorder extends ShapeBorder {
final double distanceFromWall = 12;
final double controlPointDistanceFromWall = 2;
@override
EdgeInsetsGeometry get dimensions => null;
@override
Path getInnerPath(Rect rect, {TextDirection textDirection}) {
return null;
}
@override
Path getOuterPath(Rect rect, {TextDirection textDirection}) {
return getClip(Size(220.0, 70.0));
}
@override
void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {}
@override
ShapeBorder scale(double t) {
return null;
}
Path getClip(Size size) {
Path clippedPath = Path();
clippedPath.moveTo(0 + distanceFromWall, 0);
clippedPath.quadraticBezierTo(0 + controlPointDistanceFromWall,
0 + controlPointDistanceFromWall, 0, 0 + distanceFromWall);
clippedPath.lineTo(0, size.height - distanceFromWall);
clippedPath.quadraticBezierTo(
0 + controlPointDistanceFromWall,
size.height - controlPointDistanceFromWall,
0 + distanceFromWall,
size.height);
clippedPath.lineTo(size.width - distanceFromWall, size.height);
clippedPath.quadraticBezierTo(
size.width - controlPointDistanceFromWall,
size.height - controlPointDistanceFromWall,
size.width,
size.height - distanceFromWall);
clippedPath.lineTo(size.width, size.height * 0.6);
clippedPath.quadraticBezierTo(
size.width - 1,
size.height * 0.6 - distanceFromWall,
size.width - distanceFromWall,
size.height * 0.6 - distanceFromWall - 3);
clippedPath.lineTo(0 + distanceFromWall + 6, 0);
clippedPath.close();
return clippedPath;
}
}
Here is the model class for the data:
class ContentModel {
String title;
String description;
String imglink;
int contentId;
List<String> tags;
List<String> focusAreas;
int likeCount;
String myIcon;
bool isNew;
String content;
String contentLink;
String appColor;
double positionVar;
String detailScreenLink;
String documentId;
String topic;
String hashtag;
ContentModel(
{this.title,
this.description,
this.imglink,
this.contentId,
this.tags,
this.likeCount,
this.myIcon,
this.isNew,
this.content,
this.contentLink,
this.appColor,
this.positionVar,
this.detailScreenLink,
this.documentId,
this.topic,
this.focusAreas,
this.hashtag});
Map<String, dynamic> toMap() {
return {
'title': title,
'description': description,
'imglink': imglink,
'contentId': contentId,
'tags': tags,
'likeCount': likeCount,
'isNew': isNew,
'content': content,
'contentLink': contentLink,
'appColor': appColor,
'positionVar': positionVar,
'detailScreenLink': detailScreenLink,
'documentId': documentId,
'topic': topic,
'focusAreas': focusAreas,
'hashtag': hashtag
};
}
static ContentModel fromDocument(DocumentSnapshot document) {
if (document == null || document.data == null) return null;
return ContentModel(
documentId: document.documentID,
imglink: document.data['imglink'],
title: document.data['title'],
description: document.data['description'],
likeCount: document.data['likeCount'],
tags: document.data['tags'],
isNew: document.data['isNew'],
content: document.data['content'],
contentLink: document.data['contentLink'],
appColor: document.data['appColor'],
positionVar: document.data['positionVar'],
detailScreenLink: document.data['detailScreenLink'],
topic: document.data['topic'],
focusAreas: document.data['focusAreas'],
hashtag: document.data['hashtag']);
}
Map toJson() => {
'title': title,
'description': description,
'imglink': imglink,
'contentId': contentId,
'tags': tags,
'likeCount': likeCount,
'isNew': isNew,
'content': content,
'contentLink': contentLink,
'appColor': appColor,
'positionVar': positionVar,
'detailScreenLink': detailScreenLink,
'documentId': documentId,
'topic': topic,
'focusAreas': focusAreas,
'hashtag': hashtag
};
}
I think the issue arises from the builder parameter, itemCount: expertList.length.
One possible case could be, that the expertList is not yet populated from the backend, when the widget build is being triggered. I'd suggest using a wait parameter to ensure the data has been populated before rendering the builder on screen. In my experience, I was able to achieve this functionality using a ModalProgressHud configured to my state.waiting boolean.
Another solution is to just add the null checks. Quick fix could be:
expertList.isNotEmpty ?
ListView.builder(
padding: EdgeInsets.only(left: 50, right: 50),
scrollDirection: Axis.horizontal,
itemCount: expertList.length, ... )
: Container();
This ensures that the ListView Builder is only added to the widget tree if already populated. Hence, bypasses the null issues.
Answered by Hamza Ahmad on November 27, 2021
Given
List<dynamic> dynamicList;
You can use
var stringList = List<String>.from(dlist);
to convert a List<dynamic>
to List<String>
Therefore you need to fix your mode:
static ContentModel fromDocument(DocumentSnapshot document) {
if (document == null || document.data == null) return null;
return ContentModel(
documentId: document.documentID,
imglink: document.data['imglink'],
title: document.data['title'],
description: document.data['description'],
likeCount: document.data['likeCount'],
tags: List<String>.from(document.data['tags']),// to convert a List<dynamic> to List<String>
isNew: document.data['isNew'],
content: document.data['content'],
contentLink: document.data['contentLink'],
appColor: document.data['appColor'],
positionVar: document.data['positionVar'],
detailScreenLink: document.data['detailScreenLink'],
topic: document.data['topic'],
focusAreas: List<String>.from(document.data['focusAreas']), //to convert a List<dynamic> to List<String>
hashtag: document.data['hashtag']);}
Answered by Yousif khalid on November 27, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP