In my use case i have to change the layout of the app with json data. I have a json file which i want to get and use the key values without using Future method in the next method rather i want to place the mapped json and place it in an empty curly brackets:
This is the json file i grab:
# test_json.json
{
"fontSize" : "10",
"fontFamily" : "A",
"fontWeigth" : "bold",
"fontColor" : "blue"
}
This is the file which grabs the json file and maps it:
# get_json.dart
class GetJson{
Future<Map<String, dynamic>> getJson() async {
String jsonData =
await rootBundle.loadString('assets/json/test_json.json');
Map<String, dynamic> data = jsonDecode(jsonData);
return data;
}
}
Then i grab this mapped json and i want to place it inside a variable called mappedData
and place it inisde empty curly brackets. Then i want to get the number with getNumber()
and inisde this method i convert the type of fontSize
from string to double with another custom method called TypeConvertor.getDouble()
:
class Utility {
var mappedData= {};
setJson() async {
mappedData = await GetJson().getJson();
}
getNumber(String key) {
var data = mappedData;
return TypeConvertor.getDouble(data[key]);
}
}
In my use case i need to this like this i have no other choice. I want to explicitly grab the json like that and i don't want getNumber()
to be a Future because then i cannot place Utility().getNumber("fontSize")
inside a stateful widget because then i have to use setState
and i want to avoid that because i will have alot of keys beside fontSize
and so then i have to use setState
for every key values. I just want to use Utility().getNumber("fontSize")
inside property fontSize
and The rest also like this. In my usecase i have to do it like that:
class TestView extends StatefulWidget {
const TestView({Key? key}) : super(key: key);
@override
State<TestView> createState() => _TestViewState();
}
class _TestViewState extends State<TestView> {
@override
Widget build(BuildContext context) {
return Text(
"test",
style: TextStyle(fontSize: Utility().getNumber("fontSize")),
);
}
}
But in my app mappedData
gives null. The full error is : Unhandled Exception: type 'Null' is not a subtype of type 'String'
and the null value is inside mappedData
. I want to grab the json data and place it inside an empty map and use the mapped json from there on. How do i resolve the null execption issue?
EDIT
Change variables to correct versions
CodePudding user response:
Probably it's because you don't call setJson
before call getNumber
.
The following code is work.
final utility = Utility();
await utility.setJson();
print(utility.getNumber("fontSize"));
If you want to avoid similar mistakes, you have some options as solutions.
- Include
mappedData
to Utility's constructor. - Change
getNumber
to static, and add argumentmappedData
. - Use JsonSerializable(It's a little difficult but the best solution.)
CodePudding user response:
I found the solution which is partly contributed by @bakatsuyuki. So i did use await utility.setJson();
but i also initilized it with initState()
so field utility
has no null
value. I also used FutureBuilder()
to check if snapshot.hasData
and then i display the Text()
widget with the data from utilitiy
else show empty Container
. This way i can resolve the null
Exception.
This is the view that worked for me:
class AppView extends StatefulWidget {
const AppView({
Key? key,
}) : super(key: key);
@override
_AppViewState createState() => _AppViewState();
}
class _AppViewState extends State<AppView> {
final utility = Utility();
Future setUtility() async {
await utility.setJson();
}
@override
void initState() {
setUtility();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder(
future: AppContent().getAppContent(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text("test",
style: TextStyle(
fontSize: utility.getNumber("fontSize"),
));
} else {
return Container();
}
}),
),
);
}
}