I’m having this error above when I try to compare the parameter I get from the route with the value of a list.
MealDetailScreen widget
import 'package:flutter/material.dart';
import '../dummy_data.dart';
class MealDetailScreen extends StatelessWidget {
static const detailScreenRouteName = '/meal-detail';
#override
Widget build(BuildContext context) {
final mealId = ModalRoute.of(context)!.settings.arguments;
final selectedMeal = DUMMY_MEALS.firstWhere((meal) => meal.id == mealId);
return Scaffold(
appBar: AppBar(title: Text('$mealId')),
body: Container(
child: Column(
children: [
Container(
height: 300,
width: double.infinity,
child: Image.network(
selectedMeal.imageUrl,
fit: BoxFit.cover,
),
)
],
),
),
);
}
}
If i try to add the optional argument 'orElse' to the firstWhere function i still get an error: this time it is The return type 'Null' isn't a 'Meal', as required by the closure's context.
This is the list im using to compare the id.
const DUMMY_MEALS = [
Meal(
isVegetarian: false,
isLactoseFree: false,
isVegan: false,
id: 'm1',
categories: [
'c1',
'c2',
],
title: 'Spaghetti with Tomato Sauce',
affordability: Affordability.Affordable,
complexity: Complexity.Simple,
imageUrl: '...',
duration: 20,
ingredients: ['4 Tomatoes', '...'],
steps: ['Cut the tomatoes and the onion into small pieces.', '...'],
isGlutenFree: false,
),
];
And this is how i pass the id as parameter
void selectMeal(BuildContext context) {
Navigator.of(context)
.pushNamed(MealDetailScreen.detailScreenRouteName, arguments: {id});
}
Meal model
import 'package:flutter/foundation.dart';
enum Complexity { Simple, Challenging, Hard }
enum Affordability { Affordable, Pricey, Luxurious }
class Meal {
final String id;
final List<String> categories;
final String title;
final String imageUrl;
final List<String> ingredients;
final List<String> steps;
final int duration;
final Complexity complexity;
final Affordability affordability;
final bool isGlutenFree;
final bool isLactoseFree;
final bool isVegan;
final bool isVegetarian;
const Meal(
{required this.id,
required this.categories,
required this.title,
required this.imageUrl,
required this.ingredients,
required this.steps,
required this.duration,
required this.complexity,
required this.affordability,
required this.isGlutenFree,
required this.isLactoseFree,
required this.isVegan,
required this.isVegetarian});
}
While passing arguments: {id} you are passing _HashSet<String>. But for single value id string will be enough, else use map.
In this case passing argument will be
Navigator.of(context)
.pushNamed(MealDetailScreen.detailScreenRouteName, arguments: id);
While iterating the DUMMY_MEALS list it is possible that we will get an id that is not included on DUMMY_MEALS list. In this case you can create and pass emptyMeal on orElse state or just use try catch to handle exception.
Meal? selectedMeal;
try {
final selectedMeal = DUMMY_MEALS.firstWhere((meal) => meal.id == mealId);
} catch (e) {
print(e.toString());
}
While the selectedMeal is nullable, we can check if it contains meal or not.
return Scaffold(
appBar: AppBar(title: Text('$mealId')),
body: selectedMeal == null
? Text("Cound not find data")
: Container(
child: Column(
children: [
Text(selectedMeal.title),
],
),
),
);
The answer has been given. But I want mention that if you're using pushNamed method, it recommended that you manage passing parameters with onGenerateRoute . So you don't have nullcheck arguments or context needed.
ModalRoute.of(context)?.settings?.arguments?
MaterialApp(
onGenerateRoute: (settings) {
if (settings.name == PassArgumentsScreen.routeName) {
final args = settings.arguments as ScreenArguments;
return MaterialPageRoute(
builder: (context) {
return PassArgumentsScreen(
title: args.title,
message: args.message,
);
},
);
}
assert(false, 'Need to implement ${settings.name}');
return null;
},
)
References: Pass arguments to a named route
Related
I already have a method to pass data. It can only pass one argument, but I want it to pass at least two arguments. How can I do that?
screen1
GestureDetector(
onTap: () => Navigator.of(context).push(PageTransition(settings: RouteSettings(
arguments:imageUrl,),type: PageTransitionType.fade,
child: const ShowPictureScreen())),
Receive on Screen2
#override
Widget build(BuildContext context) {
final data = ModalRoute.of(context)!.settings;
late String photoUrl;
if (data.arguments == null) {
photoUrl = "empty";
} else {
photoUrl = data.arguments as String;
}
...
Text('photoUrl')
you can pass multiple data using the following code
// screen1.dart
..
Expanded(
child: RaisedButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(
builder: (context) => new Screen2(name: thing.name, email: thing.email, address: thing.address, etc..),
),
);
},
),
),
..
// screen2.dart
class Screen2 extends StatefulWidget{
Screen2({this.name, this.email, this.address, etc..});
final String name;
final String email;
final String address;
// etc
#override
State<StatefulWidget> createState() { return new Screen2State();}
}
class Screen2State extends State<Screen2> {
Widget build(BuildContext context) {
return new WillPopScope(
..
child: Scaffold(
..
new Row(
children: <Widget>[
new Text(widget.name),
new Text(widget.email),
new Text(widget.address),
etc..
],
),
)
)
}
You can pass argements through Map
arguments: {
"imageUrl": imageUrl,
"name": name,
"email": email
}
and you can receive like this
final data = ModalRoute.of(context)!.settings.arguments as Map;
String photoUrl = data["imageUrl"];
I have problem with read my own custom API. When I try to read it a have an error :
" NoSuchMethodError: The method'[]' was called on null.
Receiver: null
Tried calling : "
My DataSource :
class MotivationQuotesRemoteDioDataSources {
Future<List<Map<String, dynamic>>?> getQuotesRespondeData() async {
final response = await Dio().get<List<dynamic>>(
'https://my-json-server.typicode.com/jargawl/json/quotes');
final listDynamic = response.data;
if (listDynamic == null) {
return null;
}
return listDynamic.map((e) => e as Map<String, dynamic>).toList();
}
}
My Repositories :
class MotivationQuotesRepositories {
MotivationQuotesRepositories(
{required this.motivationQuotesRemoteDioDataSources});
final MotivationQuotesRemoteDioDataSources
motivationQuotesRemoteDioDataSources;
Future<List<QuotesModel>> getQuotesModel() async {
final json =
await motivationQuotesRemoteDioDataSources.getQuotesRespondeData();
if (json == null) {
return [];
}
return json.map((item) => QuotesModel.fromJson(item)).toList();
}
}
My model:
class QuotesModel {
QuotesModel(
this.text,
this.author,
);
final String text;
final String author;
QuotesModel.fromJson(Map<String, dynamic> json)
: text = json['quotes']['text'],
author = json['quotes']['author'];
}
I try to use there :
class QoutesCardList extends StatelessWidget {
const QoutesCardList({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: BlocProvider(
create: (context) => QoutesCardCubit(
motivationQuotesRepositories: MotivationQuotesRepositories(
motivationQuotesRemoteDioDataSources:
MotivationQuotesRemoteDioDataSources(),
),
)..start(),
child: BlocBuilder<QoutesCardCubit, QoutesCardState>(
builder: (context, state) {
switch (state.status) {
case Status.initial:
return const Center(
child: Text('Initial state'),
);
case Status.loading:
return const Center(
child: CircularProgressIndicator(),
);
case Status.success:
return ListView(
scrollDirection: Axis.horizontal,
children: [
for (final quotesModel in state.results)
QoutesCard(quotesModel: quotesModel)
],
);
case Status.error:
return Center(
child: Text(state.errorMessage ?? 'Unknown error'),
);
}
},
),
),
);
}
}
Can someone tell me why it don't work ? It should be a simple ListView (with Containers) with same motivation quotes.
There are no 'quotes' in data:
[
{
text: "Genius is one percent inspiration and ninety-nine percent perspiration.",
author: "Thomas Edison"
},
...
]
So the model should look like this:
class QuotesModel {
QuotesModel(
this.text,
this.author,
);
final String text;
final String author;
QuotesModel.fromJson(Map<String, dynamic> json):
text = json['text'],
author = json['author'];
}
Here response is a string. Json decode it to map it
final listDynamic = json.decode(response.data);
Plus dont define the type here
final response = await Dio().get(
'https://my-json-server.typicode.com/jargawl/json/quotes');
I am trying to load a listview using flutter and dart but am having an issue, bare with me am new to flutter and learning by example https://github.com/didinj/flutter-crud-restapi-example/blob/master/lib/caseslist.dart am coming from a c# background. I obfuscated my api url to protect it it is valid my side.
class PlayerList extends StatelessWidget {
final List<Player> players;
PlayerList({Key key, this.players}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: players == null ? 0 : players.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: InkWell(
onTap: () {},
child: ListTile(
leading: Icon(Icons.person),
title: Text(players[index].firstName),
subtitle: Text(players[index].surname.toString()),
),
));
});
}
}
The issue surrounds this line.
PlayerList({Key key, this.players}) : super(key: key);
It says key does not exist.
I am loading the list view as such?.
#override
Widget build(BuildContext context) {
if (players == null) {
players = api.getAllPlayers() as List<Player>;
}
return Scaffold(
appBar: AppBar(
title: const Text("Flutter ListView"),
),
drawer: Drawer(
// Add a ListView to the drawer. This ensures the user can scroll
// through the options in the drawer if there isn't enough vertical
// space to fit everything.
child: new Center(
child: new FutureBuilder(
future: loadList(),
builder: (context, snapshot) {
return players.length > 0
? new PlayerList(players: players)
: new Center(
child: new Text('No data found, tap plus button to add!',
style: Theme.of(context).textTheme.titleLarge));
},
)),
));
}
Future loadList() {
Future<List<Player>> playersApi = api.getAllPlayers();
playersApi.then((PlayerList) {
setState(() {
this.players = PlayerList;
});
});
return playersApi;
}
}
My Api Call is
class ApiService {
final String apiUrl = "https://secreturl/api";
final String getAllPlayersEndPoint = "/GetAllPlayers/";
Future<List<Player>> getAllPlayers() async {
final getallPlayersUrl = Uri.parse(apiUrl + getAllPlayersEndPoint);
Response res = await get(getallPlayersUrl);
if (res.statusCode == 200) {
List<dynamic> body = jsonDecode(res.body);
List<Player> players =
body.map((dynamic item) => Player.fromJson(item)).toList();
return players;
} else {
throw "Failed to load cases list";
}
}
}
This is my Model
class Player {
final int id;
final int type;
final String playerLevel;
final String firstName;
final String surname;
Player(this.id, this.type, this.playerLevel, this.firstName, this.surname);
factory Player.fromJson(Map<String, dynamic> json) {
return Player(
json['id'],
json['type'],
json['playerlevel'],
json['firstname'],
json['surname'],
);
}
#override
String toString() =>
'Players{id: $id, firstName: $firstName, lastName: $surname}';
}
This is the JSON Response I'm getting. I want to Parse the text which says "url" (With. I want to Get this Text)
{
"data": [
{
"id": 1,
"attributes": {
"Title": "My Story",
"Story": "My story is about my life",
"Image": {
"data": {
"id": 1,
"attributes": {
"name": "lifeImage.jpeg",
"alternativeText": "lifeImage.jpeg",
"caption": "lifeImage.jpeg",
"url": "/uploads/lifeImage_0e9293ee8d.jpeg", <-- I want to Get this Text
"previewUrl": null,
"provider": "local"
}
}
}
}
}
],
"meta": {
"pagination": {
"page": 1,
"pageSize": 25,
"pageCount": 1,
"total": 3
}
}
}
I easily parsed the Title, Story with the Below Dart File.
But I'm not sure how can I parse the URL text (One with arrow marks says "<-- I want to Get this Text"). Since it's nested one I don't know how to do that. Please help me with this. Thanks
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';
import 'StoryPage.dart';
void main() => runApp(App());
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My Stories',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Homepage(),
);
}
}
class Homepage extends StatefulWidget {
const Homepage({Key? key}) : super(key: key);
#override
_HomepageState createState() => _HomepageState();
}
class _HomepageState extends State<Homepage> {
Future<List<Story>> _getStories() async {
var response = await http.get(Uri.parse(
'https://exampleapi.com/api/stories/?populate=Image'));
if (response.statusCode == 200) {
Map responseData = jsonDecode(response.body);
List StoriesList = responseData["data"];
List<Story> stories = [];
for (var storyMap in StoriesList) {
stories.add(Story.fromJson(storyMap["attributes"]));
}
return stories;
} else {
throw Exception('Failed to load stories');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('My Stories'),
),
body: Container(
child: FutureBuilder(
future: _getStories(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(
child: CircularProgressIndicator(),
),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
child: Card(
child: Padding(
padding: const EdgeInsets.only(
top: 32.0, bottom: 32.0, left: 16.0, right: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
snapshot.data[index].title,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
onTap: () {
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => new StoryPage(
snapshot.data[index],
title: snapshot.data[index].title,
body: snapshot.data[index].body,
)));
},
);
},
);
}
},
),
),
);
}
}
class Story {
final String title;
final String body;
Story({required this.title, required this.body});
factory Story.fromJson(Map<String, dynamic> json) {
return Story(
title: json['Title'],
body: json['Story'],
);
}
}
You can simply parse them like this:
final url = json['image']?['data']?['attributes']?['url'];
here we're using question marks (?) in between, because we're not sure that each element is not null, for example, the data under image may be null, so by using question marks, we're staying safe from null exceptions.
I took #Adnan approach and modified it little.
final ImageURL = json['Image']?['data']?['attributes']?['url'] ?? ''
Thanks to GitHub Co-Pilot.
I suggest using this website to generate dart or any other language object from json.
you just need to covert it to null safety.
here a example:
class AnswerResponse {
AnswerResponse({
required this.id,
required this.description,
required this.isAccepted,
required this.isMine,
required this.media,
required this.caseId,
required this.voteUp,
required this.voteDown,
required this.votes,
required this.user,
required this.comment,
required this.createdAt,
});
factory AnswerResponse.fromJson(final String str) => AnswerResponse.fromMap(json.decode(str));
factory AnswerResponse.fromMap(final Map<String, dynamic> json) => AnswerResponse(
id: json["id"],
description: json["description"],
isAccepted: json["isAccepted"],
isMine: json["isMine"],
media: json["media"] == null ? null : List<MediaResponse>.from(json["media"].map((final dynamic x) => MediaResponse.fromMap(x))),
caseId: json["caseId"],
voteUp: json["voteUp"],
voteDown: json["voteDown"],
votes: json["votes"],
user: json["user"] == null ? null : ProfileResponse.fromMap(json["user"]),
comment: json["comment"] == null ? null : List<CommentResponse>.from(json["comment"].map((final dynamic x) => CommentResponse.fromMap(x))),
createdAt: json["createdAt"],
);
final int? id;
final int? caseId;
final int? votes;
final bool? isAccepted;
final bool? isMine;
final bool? voteUp;
final bool? voteDown;
final String? description;
final String? createdAt;
final ProfileResponse? user;
final List<MediaResponse>? media;
final List<CommentResponse>? comment;
}
and to convert json string to dart object:
var data = AnswerResponse.fromMap(jsonString)
I am learning Dart and Flutter. Now I am tasting JSON as a persistence method. I get lots of errors, all concerning Types and stuff. This is the latest error I have experienced: _TypeError (type 'List' is not a subtype of type 'Map')
_TypeError (type 'String' is not a subtype of type 'Map')
static Resource<List<ProductImage>> get all {
return Resource(
url: Constants.HEADLINE_NEWS_URL,
parse: (response) {
String jsonProduct = response.body;
final jsonResponse = json.decode(jsonProduct);
Product product = new Product.fromJson(jsonResponse);
return product.images.toList();
//return list.map((model) => NewsArticle.fromJson(model));
}
);
}
My Model :
class Product {
final String id;
final List<ProductImage> images;
Product({this.id, this.images});
factory Product.fromJson(Map<String, dynamic> parsedJson){
var list = parsedJson['IDEPELTION'] as List;
print(list.runtimeType);
List<ProductImage> imagesList = list.map((i) => ProductImage.fromJson(i)).toList();
return Product(
id: parsedJson['DATAS'],
images: imagesList
);
}
}
class ProductImage {
final String itemName;
final String iCategory;
// bool isCheck;
ProductImage({this.itemName, this.iCategory});
factory ProductImage.fromJson(Map<String, dynamic> parsedJson){
return ProductImage(
itemName:parsedJson['ITEM_NAME'],
iCategory:parsedJson['CATEGORY'],
//isCheck:false
);
}
}
this is my sample json return the list data to get all method
{"DATAS":"1","IDEPELTION":[{"ITEM_NAME":TRUEMETRIX ,"CATEGORY":"MOUTHPIECE"},{"ITEM_NAME":MULTISTIX 10SG 2161,"CATEGORY":"MOUTHPIECE"}]}
You can copy paste run full code below
You can see related Product class in full code and parse with product = productFromJson(jsonString);
code snippet
String jsonString = '''
{"DATAS":"1",
"IDEPELTION":
[{"ITEM_NAME":"TRUEMETRIX" ,
"CATEGORY":"MOUTHPIECE"},
{"ITEM_NAME":"MULTISTIX 10SG 2161",
"CATEGORY":"MOUTHPIECE"}]}
''';
product = productFromJson(jsonString);
working demo
full code
import 'package:flutter/material.dart';
import 'dart:convert';
Product productFromJson(String str) => Product.fromJson(json.decode(str));
String productToJson(Product data) => json.encode(data.toJson());
class Product {
String datas;
List<Idepeltion> idepeltion;
Product({
this.datas,
this.idepeltion,
});
factory Product.fromJson(Map<String, dynamic> json) => Product(
datas: json["DATAS"],
idepeltion: List<Idepeltion>.from(
json["IDEPELTION"].map((x) => Idepeltion.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"DATAS": datas,
"IDEPELTION": List<dynamic>.from(idepeltion.map((x) => x.toJson())),
};
}
class Idepeltion {
String itemName;
String category;
Idepeltion({
this.itemName,
this.category,
});
factory Idepeltion.fromJson(Map<String, dynamic> json) => Idepeltion(
itemName: json["ITEM_NAME"],
category: json["CATEGORY"],
);
Map<String, dynamic> toJson() => {
"ITEM_NAME": itemName,
"CATEGORY": category,
};
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
Product product;
void _incrementCounter() {
String jsonString = '''
{"DATAS":"1",
"IDEPELTION":
[{"ITEM_NAME":"TRUEMETRIX" ,
"CATEGORY":"MOUTHPIECE"},
{"ITEM_NAME":"MULTISTIX 10SG 2161",
"CATEGORY":"MOUTHPIECE"}]}
''';
product = productFromJson(jsonString);
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
product == null
? Container()
: Expanded(
child: ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: product.idepeltion.length,
itemBuilder: (BuildContext context, int index) {
return Container(
child: Center(
child: Text(
'${product.idepeltion[index].category} ${product.idepeltion[index].itemName}')),
);
}),
),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Change:
String jsonProduct = response.body;
Into this:
Map<String,dynamic> jsonProduct = response.body;
Since the response is of type Map