Flutter Shared Preferences saving values, not displaying - sharedpreferences

I have a list of courses. The user marks each course complete using a checkbox on the ListTile.
I implemented Shared Preferences so the list of completed courses persists when the user closes the app.
The values are saving, but when the app is closed (in the emulator or through the IDE) and reopened, the UI shows the value as false (Even when the Terminal says the value is True).
When I hot restart, the UI shows the value as True (Which was expected from the start). I haven't been able to get the UI to show correctly using the emulator buttons or on a device.
How can I get the UI to show the values correctly right away?
SharedPreferences prefs;
void getResult(Course course) async {
prefs = await SharedPreferences.getInstance();
results[course.courseResult] = prefs.getBool(course.courseResult) ?? false;
print('${course.courseTitle} Result: ${results[course.courseResult]}');
setState(() {
results[course.courseResult];
});
}
Future<bool> setResult(Course course) async {
prefs = await SharedPreferences.getInstance();
print ('${course.courseTitle} SET TO ${results[course.courseResult]}');
return prefs.setBool(course.courseResult, results[course.courseResult]);
}
initState() {
super.initState();
getResult(widget.entry);
}
Future onChanged(bool value, Course course) {
setState(() {
results[course.courseResult] = value;
});
return setResult(course);
}
Here is the full code (Though I did shorten the lists for space purposes, and left out the pages that aren't affected by this error...)
import 'package:flutter/material.dart';
import 'main.dart';
import 'CourseList.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:async';
import 'package:url_launcher/url_launcher.dart';
import 'package:intl/intl.dart';
class LearningPlan extends StatefulWidget{
LearningPlanState createState() => new LearningPlanState();
}
class LearningPlanState extends State<LearningPlan> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppBar(
title: Text('Learning Plan'),
),
drawer: MyDrawer(),
body: ListView.builder(
itemBuilder: (BuildContext context, int index) =>
new CourseTile(courseList[index]),
itemCount: courseList.length,
),
);
}
}
class CourseTile extends StatefulWidget {
CourseTile(this.entry);
final Course entry;
CourseTileState createState() => new CourseTileState();
}
class CourseTileState extends State<CourseTile> {
//Detail Card
Future<Null> _launched; // ignore: unused_field
Future<Null> _launchInWebViewOrVC(String url) async {
if (await canLaunch(url)) {
await launch(url, forceSafariVC: false, forceWebView: false);
} else {
throw 'Could not launch $url';
}
}
Widget selfDirectedURL(Course course) {
if (course.courseMethod == 'Self-Directed') {
return new IconButton(
icon: Icon(Icons.cloud_download),
onPressed: () => setState(() {
_launched = _launchInWebViewOrVC(course.courseURL);
}),
);
} else {
return new Container();
}
}
Future<Null> courseDetails(Course course) async {
await showDialog(
context: context,
child: new SimpleDialog(
title: Text(course.courseTitle),
children: <Widget>[
Stack(
children: <Widget>[
Center(child: Image.asset(course.courseImage,
colorBlendMode: BlendMode.lighten,
color: fkBlue25,
height: 200.0,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(course.courseDescription),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
selfDirectedURL(course),
FlatButton(
onPressed: (){
Navigator.pop(context);
},
child: Text('OK'),
),
],
),
],
));
}
//CheckBox Constructors
SharedPreferences prefs;
void getResult(Course course) async {
prefs = await SharedPreferences.getInstance();
results[course.courseResult] = prefs.getBool(course.courseResult) ?? false;
print('${course.courseTitle} Result: ${results[course.courseResult]}');
setState(() {
results[course.courseResult];
});
}
Future<bool> setResult(Course course) async {
prefs = await SharedPreferences.getInstance();
print ('${course.courseTitle} SET TO ${results[course.courseResult]}');
return prefs.setBool(course.courseResult, results[course.courseResult]);
}
initState() {
super.initState();
getResult(widget.entry);
}
Future onChanged(bool value, Course course) async {
final result = await setResult(course);
setState(() {
results[course.courseResult] = value;
});
return result;
}
//Main Tile
Widget buildTiles(Course course) {
return Card(
shape: Border.all(
color: fkBlue,
),
margin: EdgeInsets.all(16.0),
elevation: 8.0,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
title: Text(course.courseTitle),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(course.courseCode),
Text(course.courseMethod)
],
),
leading: SizedBox(
height: 60.0,
width: 60.0,
child: Image.asset(course.courseImage)),
trailing: Column(
children: <Widget>[
Text(results[course.courseResult] ? 'Complete' : 'Incomplete',
),
Checkbox(
value: results[course.courseResult],
onChanged: (bool value) {
onChanged(value, course);
if (value == true) {
snackBarCompleted(course);
} else {
snackBarUnCompleted(course);
}
},
),
]
),
onTap: () {
courseDetails(course);
}
),),
);
}
#override
Widget build(BuildContext context) {
return buildTiles(widget.entry);
}
void snackBarCompleted(course) {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text(
'${course.courseTitle} completed on ${DateFormat.yMd().format(DateTime.now()).toString()}'
),
backgroundColor: fkBlue,
duration: Duration(seconds: 3),
),
);
}
void snackBarUnCompleted(course) {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('${course.courseTitle} no longer marked \"Complete\"'
),
duration: Duration(seconds: 3),
),
);
}
}
//Learning Schedule Page
class LearningSchedule extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppBar(
title: Text('Schedule'),
),
drawer: MyDrawer(),
body: ListView.builder(
itemBuilder: (BuildContext context, int index) =>
new LearningScheduleBuilder(courseList[index]),
itemCount: courseList.length,
),
);
}
}
class LearningScheduleBuilder extends StatelessWidget {
LearningScheduleBuilder(this.entry);
final Course entry;
Widget buildList (Course course) {
return Text(course.courseTitle,
style: new TextStyle(color: results[course.courseResult] ? Colors.grey : fkBlue),);
}
#override
Widget build(BuildContext context) {
return buildList(entry);
}
}
final List<Course> courseList = <Course>[
new Course(
courseTitle: 'Company Orientation',
coursePreReq: 'N/A',
courseCode: 'HR',
courseURL: '',
courseMethod: 'Facilitator-Led',
courseImage: 'assets/courseImage/logo.png',
courseDescription:
'Company overview; Benefits package and documents; Ethics and Compliance Training, Introduction to learning programs; Computer orientation; Lab tour; Safety training.',
courseAudience: 'BCAE BCCC ITAE ITCC TCTAE TCTCC PlasmaCC PlasmaAE',
courseResult: 'result1',
),
new Course(
courseTitle: 'Intro to Learning Program',
coursePreReq: 'N/A',
courseCode: 'Nicole Asma',
courseURL: '',
courseMethod: 'Facilitator-Led',
courseImage: 'assets/courseImage/logo.png',
courseDescription:
'Overview of onboarding program; Components of North America University; Support available for all learning units; introduction to Learning and Development Team Overview of WebEx calls.',
courseAudience: 'BCAE BCCC ITAE ITCC TCTAE TCTCC PlasmaCC PlasmaAE',
courseResult: 'result2',
),
class Course {
final String courseTitle;
final String coursePreReq;
final String courseCode;
final String courseDescription;
final String courseImage;
final String courseMethod;
final String courseURL;
final String courseAudience;
final String courseResult;
const Course({
this.courseTitle,
this.coursePreReq,
this.courseCode,
this.courseDescription,
this.courseImage,
this.courseMethod,
this.courseURL,
this.courseAudience,
this.courseResult,
});
Course.fromMap(Map<String, dynamic> map)
: courseTitle = map['courseTitle'],
coursePreReq = map['coursePreReq'],
courseCode = map['courseCode'],
courseDescription = map['courseDescription'],
courseImage = map['roocourseImagem'],
courseMethod = map['courseMethod'],
courseURL = map['courseURL'],
courseAudience = map['courseAudience'],
courseResult = map['courseResult'];
}
Map results = {
'result1': false,
'result2': false,
'result3': false,
'result4': false,

Could you make this little change? :
Change this :
Future onChanged(bool value, Course course) {
setState(() {
results[course.courseResult] = value;
});
return setResult(course);
}
To this:
Future onChanged(bool value, Course course) async {
final result = await setResult(course);
setState(() {
results[course.courseResult] = value;
});
return result;
}
UPDATE
Replace your initState method with this:
_onLayoutDone(_){
getResult(widget.entry);
}
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback(_onLayoutDone);
super.initState();
}

Related

How to dynamically populate MediaItem from firestore in audio_service?

I have been trying to dynamically populate MediaItem with some audio data from firestore.
I am using the exact plugin example, but this time mediaItems is being sourced dynamically from firestore. I have reviewed my code multiple times, but I can't figure out what I am doing wrong.
Here are my attempts:
First I fetched the song data using a StreamBuilder and passed it as a DocumentSnapshot List to the AudioServicePlayer() page.
List<DocumentSnapshot> _list;
_list = snapshot.data.docs;
Flexible(
child: ListView.builder(
itemCount: 1,
itemBuilder: (BuildContext context, int index) {
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => JustAudioPlaylist(
songs: [_list[index]],
),
));
},
child:
Container(child: Center(child: Text('My Playlists'))),
);
}),
)
I successfully received the QueryDocumentSnapshot as expected. But when I tried to populate MediaItem with widget.songs List, it returns just a blank white page with no error. I can't figure out what I am doing wrong here;
class AudioServicePlayer extends StatefulWidget {
static const String id = 'audio-service';
List<DocumentSnapshot> songs = [];
AudioServicePlayer({this.songs});
#override
_AudioServicePlayerState createState() => _AudioServicePlayerState();
}
class _AudioServicePlayerState extends State<AudioServicePlayer> {
MediaLibrary _mediaLibrary = MediaLibrary();
#override
void initState() {
_mediaLibrary._items.addAll(widget.songs
.map((song) => MediaItem(
// This can be any unique id, but we use the audio URL for convenience.
id: song['song'],
album: "Science Friday",
title: song['songTitle'],
artist: song['artist']['artistName'],
duration: Duration(milliseconds: 5739820),
artUri: Uri.parse(song['songImage']).toString(),
))
.toList());
super.initState();
}

Flutter: A non-null String must be provided to a Text widget. Failed assertion: line 378 pos 10: 'data != null'

I getting data from API and adding it to data table, it's working fine, when i select month from dropdown, my data table get modify according to selected month from drop down, but initially it print this error.
and its change to this when i select month from dropdown
here is code
class MyAttendance extends StatefulWidget {
#override
_MyAttendanceState createState() => _MyAttendanceState();
}
class _MyAttendanceState extends State<MyAttendance> {
//List<History> _historyList;
List<History> historyList=[];
String _selectedLeave;
int monthIndex;
int month;
var monthsList=<String>[
'January',
'Febuary',
'March',
'April',
'May',
'June',
'July',
'Augest',
'September',
'October',
'November',
'December'
];
String getdate="";
void _getDate() {
final String formattedDateTime =
DateFormat('MM').format(DateTime.now()).toString();
setState(() {
getdate = formattedDateTime;
print("date "+getdate);
});
}
_userDetails() async{
SharedPreferences myPrefs=await SharedPreferences.getInstance();
setState(() {
getname=myPrefs.getString('name');
});
}
void initState() {
_userDetails();
_getDate();
_getRecord();
}
Future<List<History>> _getRecord() async{
Dio dio=new Dio();
var data={
'username':getname,
'month':month
};
return dio
.post(localhostUrlAttendanceHistory,data: json.encode(data))
.then((onResponse) async {
var jsonData=onResponse.data['data'];
//List<History> historyList = [];
for (var h in jsonData) {
History history = History(
h["Date"],
h["TimeIn"],
h["TimeOut"],
);
historyList.add(history);
}
return historyList;
})
.catchError((onerror){
print(onerror.toString());
});
}
Widget attendanceHistory(List<History>
historyList)=>
DataTable(columns: <DataColumn>[
DataColumn(label: Text("Date"),),
DataColumn(label: Text("Time in" ),),
DataColumn(label: Text("Time out")
)],
rows:
historyList
?.map((element)=>DataRow(
cells: <DataCell>[
DataCell(Text(element?.date)),
DataCell(Text(element?.timeIn)),
DataCell(Text(element?.timeOut)),
]) )?.toList());
TextEditingController fromDate=new TextEditingController();
TextEditingController toDate=new TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new MyAppBar(title: Text("My Attendance"),onpressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context)=>Profile()));
}),
drawer:Emp_DrawerCode(),
body:Stack(children: <Widget>[
Container(
padding: EdgeInsets.fromLTRB(25, 15, 0, 0),
child: Text("Attendance history",style: TextStyle(fontSize: 30,color: Colors.blue[900],fontWeight: FontWeight.bold),),
),
Container(
padding: EdgeInsets.fromLTRB(45, 80, 10, 0),
child:
DropdownButton<String>(
value: _selectedLeave==null?null:monthsList[monthIndex],
items:
monthsList
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value)
);
}).toList(),
hint:Text(
"Please choose a month",
),
onChanged: (String value) {
setState(() {
_selectedLeave=value;
monthIndex = monthsList.indexOf(value);
month=monthIndex+1;
print(month);
print(_selectedLeave);
});
},
),
),
Container(
padding: EdgeInsets.fromLTRB(15, 150, 0, 0),
child:SingleChildScrollView(
scrollDirection: Axis.vertical,
child: FutureBuilder(
future: _getRecord(),
builder: (BuildContext context, AsyncSnapshot<List<History>> snapshot) {
// Check if the data has been received.
if (snapshot.hasData) {
// Return the widget and provide the received data.
return attendanceHistory(snapshot.data);
}
return Center(child: CircularProgressIndicator(),);
// print("data");
// print(snapshot.data);
// Text("No data is shown");
//return attendanceHistory(snapshot.data);
}
),
),)
]));
}
}
class History {
final String date;
final String timeIn;
final String timeOut;
History(this.date, this.timeIn, this.timeOut);
}
Error
════════ Exception caught by widgets library ═══════════════════════════════════
A non-null String must be provided to a Text widget.
'package:flutter/src/widgets/text.dart':
Failed assertion: line 378 pos 10: 'data != null'
The relevant error-causing widget was
FutureBuilder<List<History>>
lib\My_Attendance\MyAttendance.dart:171
Update:
After few seconds of red screen error, it display this, and i have not select month from dropdown too.
and it sends this error now
════════ Exception caught by widgets library ═══════════════════════════════════
The method 'map' was called on null.
Receiver: null
Tried calling: map<DataRow>(Closure: (History) => DataRow)
The relevant error-causing widget was
FutureBuilder<List<History>>
Update
Error:
Update:
builder: (BuildContext context, AsyncSnapshot<List<History>> snapshot) {
// Check if the data has been received.
if(snapshot.connectionState==ConnectionState.done){
if (snapshot.hasData) {
return Center(
child: Text('${snapshot.error} occured',
style: TextStyle(fontSize: 18)),
);
}
else if(snapshot.hasData){
return attendanceHistory(snapshot.data);
}
// Return the widget and provide the received data.
//return Center(child: CircularProgressIndicator(),);
}
//return attendanceHistory(snapshot.data);
return Center(child: CircularProgressIndicator(),);
// print("data");
// print(snapshot.data);
// Text("No data is shown");
}
),
kindly please help how i can fix it?
A better practice is to use loader while fetching the data from API. Since future builder has a property to check whether there is response from API or not. TO check this simply use
`FutureBuilder(
future: _getRecord(),
builder: (BuildContext context,
AsyncSnapshot<List<History>> snapshot) {
// Below line will check whether future has data or not
if (snapshot.connectionState == ConnectionState.done) {
// If we got an error
if (snapshot.hasError) {
return Center(
child: Text(
'${snapshot.error} occured',
style: TextStyle(fontSize: 18),
),
);
// if we got our data
} else if (snapshot.hasData) {**strong text**
if (snapshot.hasData) {
return attendanceHistory(snapshot.data);
}
return Center(child: CircularProgressIndicator(),);
}`
Or else you can use null aware operator to handle the null value as below.
Widget attendanceHistory(List<History>
historyList)=>
DataTable(columns: <DataColumn>[
DataColumn(label: Text("Date"),),
DataColumn(label: Text("Time in" ),),
DataColumn(label: Text("Time out")
)],
rows:
historyList
?.map((element)=>DataRow(
cells: <DataCell>[
DataCell(Text(element?.date)),
DataCell(Text(element?.timeIn)),
DataCell(Text(element?.timeOut)),
])
)?.toList());
This error generate when you pass a null value to Text widget. So to avoid this either you can put a null check condition before displaying text or you can pass a default value to the Text widget.
Widget attendanceHistory(List<History> historyList)=>
DataTable(columns: <DataColumn>[
DataColumn(label: Text("Date"),),
DataColumn(label: Text("Time in" ),),
DataColumn(label: Text("Time out")
)],
rows:
historyList
.map((element)=>DataRow(
cells: <DataCell>[
DataCell(Text(element.date ?? "N/A")),
DataCell(Text(element.timeIn ?? "N/A")),
DataCell(Text(element.timeOut ?? "N/A")),
])
).toList());
Display CircularProgressIndicator while fetching the records.
Container(
padding: EdgeInsets.fromLTRB(15, 150, 0, 0),
child:SingleChildScrollView(
scrollDirection: Axis.vertical,
child: FutureBuilder( //This is line 171
future: _getRecord(),
builder: (BuildContext context, AsyncSnapshot<List<History>> snapshot) {
// Check if the data has been received.
if (snapshot.hasData) {
// Return the widget and provide the received data.
return attendanceHistory(snapshot.data);
}
return Center(child: CircularProgressIndicator(),);
}
),

Flutter: Snapshot isn't showing data

I create a Listview builder with get method (API call). API call is fine, cause I get response. But in widget snapshot.data show me null. I can't fixed this problem and don't why it's behaving like this. Please someone help me.
API responsebody
Here is my code for API call
class APIService {
Future<List<EducationInfo>> getEducationInfo() async {
String url = "$baseAPIUrl/educations";
String _token = await SavedData().loadToken();
String authorization = "Bearer $_token";
var response = await http.get(url, headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
"Authorization": authorization
});
print('API ${response.statusCode}\n API${json.decode(response.body)}');
if (response.statusCode == 200) {
var jsonResponse = response.body;
var decoded = json.decode(jsonResponse);
return decoded['success']
.map<EducationInfo>((b) => EducationInfo.fromJson(b))
.toList();
} else {
throw Exception('Failed to load Education Information');
}
}
}
Here is my Model.dart
//Model
class EducationInfo {
int id;
String degreeName;
int rollNumber;
int regNumber;
int passingYear;
String gradeType;
double cgpa;
double outOfCgpa;
String divisionName;
String groupName;
String subjectName;
String boardName;
String instituteName;
EducationInfo({
this.id,
this.degreeName,
this.rollNumber,
this.regNumber,
this.passingYear,
this.gradeType,
this.cgpa,
this.outOfCgpa,
this.divisionName,
this.groupName,
this.subjectName,
this.boardName,
this.instituteName,
});
factory EducationInfo.fromJson(Map<String, dynamic> json) {
return EducationInfo(
id: json['user_id'],
degreeName: json['degree_name'],
rollNumber: json['roll_number'],
regNumber: json['registration_number'],
passingYear: json['passing_year'],
gradeType: json['grade_type'],
cgpa: json['cgpa'],
outOfCgpa: json['out_of_cgpa'],
divisionName: json['division'],
groupName: json['group_name'],
subjectName: json['subject_name'],
boardName: json['board'],
instituteName: json['institute_name'],
);
}
}
And here is my main code-
class Resume extends StatefulWidget {
#override
_ResumeState createState() => _ResumeState();
}
class _ResumeState extends State<Resume> {
Future<List<EducationInfo>> furuteEducationInfo;
#override
void initState() {
super.initState();
furuteEducationInfo = APIService().getEducationInfo();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomPadding: false,
appBar: AppBar(
automaticallyImplyLeading: false,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
),
onPressed: () {
Navigator.pop(context);
},
),
title: Text("Resume"),
),
body: Align(
child: FutureBuilder(
future: furuteEducationInfo,
builder: (context, snapshot) {
var educationInfo = snapshot.data;
if (snapshot.data == null) {
return Text("No Data Available ");
} else if (snapshot.hasData) {
return ListView.builder(
scrollDirection: Axis.vertical,
itemCount: educationInfo.length,
itemBuilder: (context, index) {
var eduInfo = educationInfo[index];
print(
"\nEducation Info ${educationInfo[index]}");
return designedContainer(
_width - 30,
Padding(
padding: EdgeInsets.all(5.0),
child: Stack(
children: [
Container(
child: Column(
children: [
detailsField(
"Degree Name",
"${_checkNull(eduInfo.degreeName)}"),
],
),
),
Align(
alignment:
Alignment.topRight,
child: editButton(
Icons.add, "Delete",
() {
print("Delete");
}),
)
],
),
));
});
} else {
Text("Something Went to Wrong");
}
}),
),
);
}
And here is also postman Screenshot-
in your EducationInfo.fromJson method
replace
cgpa: json['cgpa'] ?? 0.0,
outOfCgpa: json['out_of_cgpa'] ?? 0.0,
Since it is a future and runs Asynchronously you have to check if the snapshot has data, and wait for the data to be fetched. Following a snippet code how you would check for that inside your build method:
(snapshot.hasData && snapshot.data.length == 0)
? Container(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Congrats, No jobs asssigned!!!")
],
),
),
)
: (snapshot.hasData)
? Container(
child: ListView.builder(
.....)

data!= null : A non-null String must be provided to a Text widget

I am new to Flutter and I am getting this error: A non-null String must be provided to a Text widget. I tried to fix it but it won't. The api responds fine, here is the url: https://appiconmakers.com/demoMusicPlayer/API/getallcategories
Here is the code that I wrote,
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:async';
import 'dart:convert';
class CategoryTab extends StatefulWidget {
#override
_CategoryTabState createState() => _CategoryTabState();
}
class _CategoryTabState extends State<CategoryTab> {
#override
void initState() {
// TODO: implement initState
super.initState();
}
Future<List<CategoryList>> _getUsers() async {
var data = await http.get("https://appiconmakers.com/demoMusicPlayer/API/getallcategories");
var jsonData = json.decode(data.body);
List<CategoryList> cat = [];
for (var u in jsonData) {
CategoryList categoryList = CategoryList(u["categoryId"], u["categoryName"], u["parentCategoryId"], u["categoryStatus"], u["createdDate"]);
cat.add(categoryList);
}
print("=================================");
print(cat.length);
return cat;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Categories", style: TextStyle(color:Colors.black)),
backgroundColor: Colors.white,
), body:
Container(
child: FutureBuilder(
future: _getUsers(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
print(snapshot.data);
if (snapshot.data == null) {
return Container(
child: Center(child: Text("Loading..."))
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: CircleAvatar(),
title: Text(snapshot.data[index].categoryName,
// subtitle: Text(snapshot.data[index].categoryId),
);
},
);
}
),
),
);
}
}
class CategoryList {
String categoryId;
String categoryName;
String parentCategoryId;
String categoryStatus;
String createdDate;
CategoryList(this.categoryId, this.categoryName, this.parentCategoryId, this.categoryStatus, this.createdDate);
}
The debug section also gives me this result:
[ ] flutter: =================================
[ ] [DEVICE LOG] 2020-07-29 08:05:00.688910+0300 localhost Runner[20673]: (Flutter) flutter: 9
[ ] flutter: 9
[ ] [DEVICE LOG] 2020-07-29 08:05:00.690942+0300 localhost Runner[20673]: (Flutter) flutter:
[Instance of 'CategoryList', Instance of 'CategoryList', Instance of 'CategoryList', Instance of
'CategoryList', Instance of 'CategoryList', Instance of 'CategoryList', Instance of 'CategoryList',
Instance of 'CategoryList', Instance of 'CategoryList']
[ ] flutter: [Instance of 'CategoryList', Instance of 'CategoryList', Instance of 'CategoryList',
Instance of 'CategoryList', Instance of 'CategoryList', Instance of 'CategoryList', Instance of
'CategoryList', Instance of 'CategoryList', Instance of 'CategoryList']
[ +1 ms] [DEVICE LOG] 2020-07-29 08:05:00.692566+0300 localhost Runner[20673]: (Flutter) flutter:
Another exception was thrown: A non-null String must be provided to a Text widget.
[ ] flutter: Another exception was thrown: A non-null String must be provided to a Text widget.
just check the answer
model class for the json:
// To parse this JSON data, do
//
// final categoryList = categoryListFromJson(jsonString);
import 'dart:convert';
List<CategoryList> categoryListFromJson(String str) => List<CategoryList>.from(json.decode(str).map((x) => CategoryList.fromJson(x)));
String categoryListToJson(List<CategoryList> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class CategoryList {
CategoryList({
this.categoryId,
this.categoryName,
this.parentCategoryId,
this.categoryStatus,
this.createdDate,
this.subcategory,
});
String categoryId;
String categoryName;
String parentCategoryId;
String categoryStatus;
DateTime createdDate;
List<CategoryList> subcategory;
factory CategoryList.fromJson(Map<String, dynamic> json) => CategoryList(
categoryId: json["category_id"],
categoryName: json["category_name"],
parentCategoryId: json["parent_category_id"],
categoryStatus: json["category_status"],
createdDate: DateTime.parse(json["created_date"]),
subcategory: List<CategoryList>.from(json["subcategory"].map((x) => CategoryList.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"category_id": categoryId,
"category_name": categoryName,
"parent_category_id": parentCategoryId,
"category_status": categoryStatus,
"created_date": createdDate.toIso8601String(),
"subcategory": List<dynamic>.from(subcategory.map((x) => x.toJson())),
};
}
ui for it :
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:json_parsing_example/models.dart';
import 'package:http/http.dart' as http;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: HomePage());
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
double value;
#override
void initState() {
super.initState();
}
Future<List<CategoryList>> _getUsers() async {
var data = await http
.get("https://appiconmakers.com/demoMusicPlayer/API/getallcategories");
final categoryList = categoryListFromJson(data.body);
List<CategoryList> cat = [];
categoryList.forEach((element) {
cat.add(CategoryList(
categoryId: element.categoryId,
categoryName: element.categoryName,
parentCategoryId: element.parentCategoryId,
categoryStatus: element.categoryStatus,
createdDate: element.createdDate.toString()));
});
print("=================================");
print(cat.length);
return cat;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Categories",
style: TextStyle(color: Colors.black),
),
backgroundColor: Colors.white,
),
body: Container(
child: FutureBuilder(
future: _getUsers(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
print(snapshot.data);
if (snapshot.data == null) {
return Container(child: Center(child: Text("Loading...")));
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: CircleAvatar(),
title: Text(
snapshot.data[index].categoryName,
// subtitle: Text(snapshot.data[index].categoryId
),
);
},
);
}
},
),
),
);
}
}
class CategoryList {
String categoryId;
String categoryName;
String parentCategoryId;
String categoryStatus;
String createdDate;
CategoryList(
{this.categoryId,
this.categoryName,
this.parentCategoryId,
this.categoryStatus,
this.createdDate});
}
Let me know if it works.
Replace this code
CategoryList categoryList = CategoryList(u["categoryId"], u["categoryName"], u["parentCategoryId"], u["categoryStatus"], u["createdDate"]);
with
CategoryList categoryList = CategoryList(u["category_id"], u["category_name"], u["parent_category_id"], u["category_status"], u["created_date"]);
Just do the following updates:
title: Text(snapshot.data[index].categoryName ?? '',
// subtitle: Text(snapshot.data[index].categoryId
),
Now if the snapsho.data[index].categoryName is null, the empty string will be assigned to the text widget.
In case you want to see if data is there or not, then just print the snapshot.data and you can find out if the data is there or not.
Replace
title: Text(snapshot.data[index].categoryName,
with
title: Text(snapshot.data[index].categoryName ?? “Some Text To Display if Null”
That should do it. The value after the ?? will replace the previous value if it’s null
Check this answer for more info on the ?? operator

Trying to make radio buttons with using Streambuilder and Bloc in Flutter. but don't work

I tried to make radiobuttons by using Streambuilder and Bloc.
so I made streamcontroler and when radiobuttons clicked,
I made streamcontrl.add(value) implemented, but Streambuilder don't listen
that stream. I tested onchanged value of radio. and
Please figure out what's wrong with it.
This is full code.
import 'package:flutter/material.dart';
import 'dart:async';
void main(){
runApp(new MaterialApp(
home: new MyApp(),
));
}
class bloc {
StreamController <int> ctrl = StreamController() ;
get blocvalue => ctrl.stream;
void setvalue (value ) {
ctrl.sink.add(value) ; }
}
class MyApp extends StatefulWidget {
#override
_State createState() => new _State();
}
class _State extends State<MyApp>{
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Name here'),
),
body: new Container(
padding: new EdgeInsets.all(15.0),
child: new Center(
child: new Column(
children: <Widget>[
StreamBuilder(
stream: bloc().blocvalue,
initialData: 0,
builder: (BuildContext context, AsyncSnapshot <int> snapshot)
{
List<Widget> list = new List<Widget>();
for(int i = 0; i < 3; i++){
list.add(new RadioListTile(
value: i,
groupValue: snapshot.data,
onChanged: bloc().setvalue,
activeColor: Colors.green,
controlAffinity: ListTileControlAffinity.trailing,
title: new Text('Item: ${i}'),
dense: true,
// subtitle: new Text('sub title'),
));
}
return Column(children: list,); })
],
),
),
),
);
}
}
As mentioned by pksink in the comments, you're creating a new bloc inside build by calling setting it in StreamBuilder as bloc().blocValue. What you can do here is declare it as final myBloc = bloc(); outside of the bloc and set it on your StreamBuilder as myBloc.blocValue. With this, a new instance of bloc won't be created with every rebuild of Widget build.