Related
ihave this error from server response
this is the models and api
u can find full source code here https://github.com/alihassan75/project
// To parse this JSON data, do
//
// final task = taskFromJson(jsonString);
import 'dart:collection';
import 'dart:core';
import 'package:flutter/foundation.dart';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
//List<Task> taskFromJson(String str) => List<Task>.from(json.decode(str).map((x) => Task.fromJson(x)));
//String taskToJson(List<Task> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Project {
Project({
this.id,
required this.project_name,
this.created_at,
// required this.start_date,
//required this.end_date,
required this.user,
});
int? id;
final String? project_name;
DateTime? created_at;
// final DateTime? start_date;
//final DateTime? end_date;
int user;
factory Project.fromJson(Map<String, dynamic> json) => Project(
id: json["id"],
project_name: json["project_name"],
created_at: DateTime.parse(json["created_at"]),
// start_date: DateTime.parse(json["start_date"]),
//end_date: DateTime.parse(json["end_date"]),
user: json["user"],
);
Map<String, dynamic> toJson() => {
"id": id,
"project_name": project_name,
"created_at": created_at?.toIso8601String(),
// "start_date": start_date?.toIso8601String(),
//"end_date": end_date?.toIso8601String(),
"user": user,
};
}
class ProjectProvider with ChangeNotifier{
ProjectProvider(){
this.fetchProject();
}
List<Project> _project = [];
List<Project> get project {
return [..._project];
}
void addProject(Project project) async {
final response = await http.post(Uri.parse('http://mostafahamed.pythonanywhere.com/project/api'),
headers: {"Content-Type": "application/json"}, body: json.encode(project));
if (response.statusCode == 201) {
project.id = json.decode(response.body)['id'];
_project.add(project);
notifyListeners();
print('sucess');
}
else {
print(response.body);
throw Exception('Failed to add project');
}
}
void deleteProject(Project project) async {
final response =
await http.delete(Uri.parse('http://mostafahamed.pythonanywhere.com/project/api${project.id}/'));
if (response.statusCode == 204) {
_project.remove(project);
notifyListeners();
print('sucess');
}
else {
throw Exception('Failed to load tasks');
}
}
fetchProject() async{
final response = await http
.get(Uri.parse('http://mostafahamed.pythonanywhere.com/project/api?format=json'));
if (response.statusCode==200){
var data = json.decode(response.body)as List;
_project=data.map<Project>((json) => Project.fromJson(json)).toList();
notifyListeners();
print('sucess');
}
else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load projects');
}
}
}
here i call back the post method with adding data function
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:project/model/project_model.dart';
//import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:project/model/user_Model.dart';
import 'home_page02.dart';
class NewProject extends StatefulWidget {
const NewProject({Key? key}) : super(key: key);
#override
State<NewProject> createState() => _NewProjectState();
}
class _NewProjectState extends State<NewProject> {
final TextEditingController projectname = TextEditingController();
//TextEditingController datelController = TextEditingController();
//DateTime _selectedDate = DateTime.now();
//DateTime currentDate = DateTime.now();
// Future<void> _selectDate(BuildContext context) async {
// final DateTime? pickedDate = await showDatePicker(
// context: context,
// initialDate: currentDate,
// firstDate: DateTime(2015),
// lastDate: DateTime(2050));
// if (pickedDate != null && pickedDate != currentDate)
// setState(() {
// currentDate = pickedDate;
// });
// else print('empty')
// ;}
void onAdd() {
//final DateTime? textVal = DateTime.parse(datelController.text);
final String desVal = projectname.text;
if (/*textVal != null&&*/ desVal.isNotEmpty) {
final Project project = Project(/*end_date: textVal,*/ project_name: desVal, user: 1, /*start_date: null,*/ );
Provider.of<ProjectProvider>(context, listen: false).addProject(project);
print('$desVal in sucess');
}
else{
print('empty or vaild');
//print('$textVal in vaild');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
// App Bar The upper part in the application
appBar: AppBar(
backgroundColor:const Color(0xff076792),
title: const Padding(
padding: EdgeInsets.only(
left: 50.0,
),
// project Name
child: Text(
'New Project',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 25.0,
shadows: [
Shadow(
color: Color(0xa6A2B6D4),
offset: Offset(7,5),
blurRadius:20),
]
),
),
),
iconTheme: const IconThemeData(
color:Colors.white,
size: 30.0,
),
),
body: SingleChildScrollView(
child: Column(
children: [
const Padding(
padding: EdgeInsets.only(
left: 10.0,
top: 30.0,
),
child: Padding(
padding: EdgeInsets.only(right: 80.0),
child: Text(
'Creating New Project',
style: TextStyle(
fontSize: 25.0,
color: Color(0xff076792),
fontWeight: FontWeight.bold,
),
),
),
),
Container(
margin: const EdgeInsets.fromLTRB(43, 24, 43, 15),
child: SizedBox(
height: 45,
child: TextField(
controller: projectname,
decoration: const InputDecoration(
hintText: "Enter project Name", //اليوسر يدخل اسم المشروع
hintStyle: TextStyle(
fontSize: 20,
color: Color(0xffc9c9c9), /*height: 2.5*/
),
enabledBorder: OutlineInputBorder(
borderSide:
BorderSide(width: 2, color: Color(0xFF09679a))),
),
keyboardType: TextInputType.name,
),
),
),
// Container(
// margin: const EdgeInsets.fromLTRB(43, 0, 43, 20),
// child: SizedBox(
// height: 45,
// child: TextField(
// controller: datelController,
// onTap: () {
// showDatePicker(
// context: context,
// initialDate: DateTime.now(),
// firstDate: DateTime.now(),
// lastDate: DateTime(2050),
// ).then((value) {
// datelController.text = DateFormat("yyyy-MM-dd").format(value!);
// });
// },
// decoration: const InputDecoration(
// hintText: "End Date",
// hintStyle: TextStyle(
// fontSize: 20,
// color: Color(0xffc9c9c9),
// ),
// enabledBorder: OutlineInputBorder(
// borderSide: BorderSide(width: 2, color: Color(0xFF09679a))),
// ),
// keyboardType: TextInputType.datetime,
// ),
// ),
// ),
const SizedBox(
height: 10.0,
),
Container(
width: double.infinity,
height: 54,
margin: const EdgeInsets.fromLTRB(75, 0, 75, 0),
child: ElevatedButton(
onPressed: () {
onAdd();
//_selectDate(context);
//Navigator.of(context).pop();
},
child: const Text(
'Create',
style: TextStyle(fontSize: 26, fontWeight: FontWeight.w400),
),
style: ElevatedButton.styleFrom(
primary: const Color(0xFF09679a),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
// f... flutter buttons!
side: const BorderSide(color: Colors.black, width: 1),
)),
),
),
Container(
alignment: Alignment.center,
child: const Padding(
padding: EdgeInsets.only(left: 60, top: 15.0, right: 60.0),
child: Text(
'By Creating This Project You Will Be Admin Of The Project',
style: TextStyle(
fontSize: 18.0,
fontStyle: FontStyle.italic,
color: Colors.black,
),
),
), //just text
),
// Text(currentDate.toString()),
// ElevatedButton(
// onPressed: () => _selectDate(context),
// child: Text('Select date'),
// ),
],
),
),
);
}
}
this app made to login users and view projects and add projects like todo app on the django server i can add project and view projects regstring with admin mail from python backend server .. in this app i can only view projects but cant add new project its my project for graduation so my life depends on this :D
full source code https://github.com/alihassan75/project
You are not getting the data "user" because you are not saving the "user" after login.
You need to save the user in any local storage. For, the demo purpose I will be saving that on HIVE
Import the hive package in pubspec.yaml
hive: ^2.1.0
In the main function(before run app) initialize and open a box "user"
await Hive.initFlutter();
await Hive.openBox('User');
You can push to user box in login function like this.
box = Hive.box('User');
int userId = responseJson['user']['id'];
box.put('user_id', userId);
You can read the user when ever you want like this.
late Box box;
late int userid;
box = Hive.box('User');
super.initState();
final data = box.get('user_id');
Now you can pass the data to the http api call.
Thank you
im tring to view tasks on alone screen from data using rest api dart django
with project_id its one-to-many relationship project has many tasks so i want to dispay the tasks when i click on project widget using future builder
Edit :i found the problem check my answer below
here is usage of future builder and task list
import 'package:flutter/material.dart';
import 'package:project/model/task_model.dart';
import 'package:project/project_dtetailForTest.dart';
import 'package:provider/provider.dart';
import 'new_project.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
class TaskById extends StatefulWidget {
//const TaskById({ Key? key }) : super(key: key);
final int project_id;
//final Task task =Task();
TaskById({required this.project_id}):super();
#override
State<TaskById> createState() => _TaskByIdState();
}
class _TaskByIdState extends State<TaskById> {
#override
Widget build(BuildContext context) {
final taskP= Provider.of<TaskProvider>(context);
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
elevation: 0,
bottom: PreferredSize(
child: Container(
color: const Color(0xff94adb4),
height: 2,
width: 320,
),
preferredSize: const Size.fromHeight(4.0)),
centerTitle: true,
backgroundColor:const Color(0xff076792),
title: const Text(
'Project1',
style: TextStyle(color: Colors.white,
fontSize: 35,
fontWeight:FontWeight.w700,
shadows: [
Shadow(
color: Color(0xa6A2B6D4),
blurRadius:20),
] ),
),
leading: IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: const Icon(
Icons.arrow_back,
size: 44,
),
),
actions:[Container(padding: const EdgeInsets.fromLTRB(0, 0, 6, 3),
child: IconButton(
color:Colors.white ,
icon: const Icon(
Icons.search,
size: 44,
),
onPressed: () {},
),
),]
),
//TaskProvider(){
// this.fetchTask();
// }
body: FutureBuilder(
future: TaskProvider().fetchTask(widget.project_id),
builder: (context, snapshot){
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData && snapshot is List<Task>
?TaskList(task:snapshot.data as List<Task>)
:Center(child: CircularProgressIndicator());
}),
// FutureProvider(
// create: (context) => TaskProvider().fetchTask() ,
// initialData: [CircularProgressIndicator()],
// child:Center(child: Consumer<Task>(
// builder: (context, task, widget) {
// return (taskP !=null) ? Column(
// children: [
// Row(
// children: const [
// Padding(padding: EdgeInsets.all(7),
// child: Padding(
// padding: EdgeInsets.fromLTRB(24,10,10,8),
// child: Text('Project1',
// style: TextStyle(
// color: Color(0xff076792),
// fontSize: 40,
// fontWeight: FontWeight.w600
// ),
// ),
// ),
// ),
// ],
// ),
// // dynamic list
// Expanded(
// child: ListView.builder(
// itemCount: taskP.task.length ,
// itemBuilder: (BuildContext context, int index) {
// return Padding(
// padding: const EdgeInsets.fromLTRB(28, 12, 28, 0),
// child: GestureDetector(
// onTap: () {
// Navigator.push(context, MaterialPageRoute(builder: (context) => projectDetails()));
// },
// child: Card(child:Container(padding: const EdgeInsets.fromLTRB(18,8,9,4),
// width: 100,
// height: 100,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(19.0),
// color: const Color(0xff076792),
// boxShadow: const [
// BoxShadow(color: Colors.black, spreadRadius: 1),
// BoxShadow(color: Color(0xa6A2B6D4),
// offset: Offset(7,5),
// blurRadius:20),
// ],
// ),
// child: Column(children: [
// Row(mainAxisAlignment: MainAxisAlignment.start,
// children: [
// Text('Starting '+taskP.task[index].start.toString(),style:
// const TextStyle(color: Color.fromARGB(255, 133, 186, 202),fontSize: 18,),
// ),
// ],
// ),
// const Spacer(),
// Row(mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Text(taskP.task[index].title??'',style:
// const TextStyle(color: Colors.white,fontSize: 34,)),
// ],
// ),
// const Spacer(),
// Row(mainAxisAlignment: MainAxisAlignment.end,
// children: [
// Text('Ending '+taskP.task[index].end.toString(),style:
// const TextStyle(color: Color.fromARGB(255, 133, 186, 202),
// fontSize: 18,
// )),
// ],
// ),
// ]),
// ),),
// ),
// );
// }),
// )
// ],
// )
// : CircularProgressIndicator();
// },
// ) ,
// )
// // widget.project_id),
// // builder: (context, snapshot){
// // if (snapshot.hasError) print(snapshot.error);
// // return snapshot.hasData
// // ? TaskList(taskP: snapshot.data)
// // : Center(child: CircularProgressIndicator());
// // }),
// )
);
}
}
class TaskList extends StatelessWidget {
final List <Task> task;
TaskList({Key? key, required this.task}) : super(key: key);
#override
Widget build(BuildContext context) {
final taskP= Provider.of<TaskProvider>(context );
// TODO: implement build
return Column(
children: [
Row(
children: const [
Padding(padding: EdgeInsets.all(7),
child: Padding(
padding: EdgeInsets.fromLTRB(24,10,10,8),
child: Text('Project1',
style: TextStyle(
color: Color(0xff076792),
fontSize: 40,
fontWeight: FontWeight.w600
),
),
),
),
],
),
// dynamic list
Expanded(
child: ListView.builder(
itemCount: taskP.task.length ,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.fromLTRB(28, 12, 28, 0),
child: GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => projectDetails()));
},
child: Card(child:Container(padding: const EdgeInsets.fromLTRB(18,8,9,4),
width: 100,
height: 100,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(19.0),
color: const Color(0xff076792),
boxShadow: const [
BoxShadow(color: Colors.black, spreadRadius: 1),
BoxShadow(color: Color(0xa6A2B6D4),
offset: Offset(7,5),
blurRadius:20),
],
),
child: Column(children: [
Row(mainAxisAlignment: MainAxisAlignment.start,
children: [
Text('Starting '+taskP.task[index].start.toString(),style:
const TextStyle(color: Color.fromARGB(255, 133, 186, 202),fontSize: 18,),
),
],
),
const Spacer(),
Row(mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(taskP.task[index].title??'',style:
const TextStyle(color: Colors.white,fontSize: 34,)),
],
),
const Spacer(),
Row(mainAxisAlignment: MainAxisAlignment.end,
children: [
Text('Ending '+taskP.task[index].end.toString(),style:
const TextStyle(color: Color.fromARGB(255, 133, 186, 202),
fontSize: 18,
)),
],
),
]),
),),
),
);
}),
)
],
);
}
}
here the api and model of task
// To parse this JSON data, do
//
// final task = taskFromJson(jsonString);
import 'dart:collection';
import 'dart:core';
import 'package:flutter/foundation.dart';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
//List<Task> taskFromJson(String str) => List<Task>.from(json.decode(str).map((x) => Task.fromJson(x)));
//String taskToJson(List<Task> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Task {
String? url;
int? id;
String? owner;
String? project;
String? title;
DateTime? start;
DateTime? end;
String? desc;
int? project_id;
Task(
{this.url,
this.id,
this.owner,
this.project,
this.title,
this.start,
this.end,
this.desc,
this.project_id});
factory Task.fromJson(Map<String, dynamic> json) => Task(
url :json["url"],
id: json["id"],
owner: json["owner"],
project: json["project"],
title: json["title"],
start: DateTime.parse(json["start"]),
end: DateTime.parse(json["end"]),
desc: json["desc"],
project_id: json["project_id"],
);
Map<String, dynamic> toJson() => {
"url": url,
"id": id,
"owner": owner,
"project": project,
"title": title,
"start": start?.toIso8601String(),
"end": end?.toIso8601String(),
"desc": desc,
"project_id": project_id,
};
}
class TaskProvider with ChangeNotifier{
TaskProvider(){
this.fetchTask();
}
List<Task> _task = [];
List<Task> get task {
return [..._task];
}
void addTask(Task task) async {
final response = await http.post(Uri.parse('http://mostafahamed.pythonanywhere.com/project/task'),
headers: {"Content-Type": "application/json"}, body: json.encode(task));
if (response.statusCode == 201) {
task.id = json.decode(response.body)['id'];
_task.add(task);
notifyListeners();
print('suceeed add ed task');
print(response.body);
}else{
print(response.body);
print('failed add ed task');
}
}
void deleteTask(Task task) async {
final response =
await http.delete(Uri.parse('http://mostafahamed.pythonanywhere.com/project/task${task.id}/'));
if (response.statusCode == 204) {
_task.remove(task);
notifyListeners();
}
}
Future<List<Task>> fetchTask([project_id]) async{
final response = await http
.get(Uri.parse('http://mostafahamed.pythonanywhere.com/tasks/?format=json'));
if (response.statusCode==200){
var data = json.decode(response.body)as List;
_task=data.map<Task>((json) => Task.fromJson(json)).toList();
notifyListeners();
print('sucess view task list');
print(response.body);
return _task;
}
else {
print(response.body);
print(response.statusCode);
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load tasks');
}
}
}
when i click on project wiget the screen keeps loading
I removed a condition && snapshot is List from future builder
and it works perfectly but
make sure to prevent the use of future builder if you have changeable values as future builder fetch data once I'm right now working to convert future builder into stream builder
if u have the same problem make sure all the types are the same
good luck to you all : )
I need to detect when my phoneCall variable changes state this to detect when the call ended and show a modal to the user so he can rate the call. This is the widget code.
import 'dart:convert';
import 'package:avanx/flutter/loading_view.dart';
import 'package:avanx/globals.dart';
import 'package:avanx/models/group.dart';
import 'package:avanx/models/prospect.dart';
import 'package:cool_alert/cool_alert.dart';
import 'package:flutter_phone_state/flutter_phone_state.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:url_launcher/url_launcher.dart';
import '../flutter/theme.dart';
import 'package:avanx/flutter/input_widget.dart';
import '../flutter/widgets.dart';
import 'package:flutter/material.dart';
import 'package:avanx/flutter/drawer_widget.dart';
import 'package:loading_animations/loading_animations.dart';
import 'package:http/http.dart' as http;
class LeadsHomeWidget extends StatefulWidget {
LeadsHomeWidget({Key key}) : super(key: key);
#override
_LeadsHomeWidgetState createState() => _LeadsHomeWidgetState();
}
class _LeadsHomeWidgetState extends State<LeadsHomeWidget> {
List<dynamic> _leads = [];
List<dynamic> _groups;
var phoneCall;
TextEditingController textController;
final scaffoldKey = GlobalKey<ScaffoldState>();
GlobalKey<FormState> _formKey = GlobalKey();
String _search = '';
_initCall(_number) async {
setState(() {
phoneCall = FlutterPhoneState.startPhoneCall(_number);
});
}
showCallInfo() {
print(phoneCall
.status); // ringing, dialing, cancelled, error, connecting, connected, timedOut, disconnected
print(phoneCall.isComplete); // Whether the call is complete
print(phoneCall
.events); // A list of call events related to this specific call
}
// Metodo para la obtención de los grupos existentes.
getGroups() async {
print("hello");
final response = await http.get(Uri.parse(Globals.avanx + '/groups'),
headers: {"Accept": "application/json"});
if (response.statusCode == 200) {
var groupsList = json.decode(response.body)['groups'];
List<dynamic> groups =
groupsList.map((element) => new Group.fromJson(element)).toList();
setState(() {
_groups = groups;
});
} else {
throw Exception('Failed to load groups');
}
}
getLeads(name) async {
if (name != '') {
String auth = Globals.authUser.user.id.toString();
final response = await http.post(
Uri.parse(Globals.avanx + '/prospects/search/$auth'),
body: {'name': name},
headers: {"Accept": "application/json"});
if (response.statusCode == 200) {
var leadsList = json.decode(response.body)['leads'];
List<dynamic> leads =
leadsList.map((element) => new Prospect.fromJson(element)).toList();
setState(() {
_leads = leads;
});
} else {
throw Exception('Failed to load leads');
}
} else {
setState(() {
_leads = [];
});
}
}
deleteModal(id) {
CoolAlert.show(
context: context,
animType: null,
type: CoolAlertType.warning,
title: '¿Estas Seguro?',
text: "Esta acción no se puede reversar.",
backgroundColor: Color(0xBF35126A),
confirmBtnText: 'Confirmar',
confirmBtnColor: Color(0xBF35126A),
showCancelBtn: true,
cancelBtnText: 'Cancelar',
onConfirmBtnTap: () {
deleteLead(id);
Navigator.pop(context);
});
}
deleteLead(id) async {
final response = await http.delete(
Uri.parse(Globals.avanx + '/prospects/$id'),
headers: {"Accept": "application/json"});
if (response.statusCode == 200) {
_search = '';
this.getLeads(_search);
} else {
throw Exception('Failed to load leads');
}
}
#override
void initState() {
textController = TextEditingController();
this.getGroups();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
backgroundColor: FlutterTheme.tertiaryColor,
appBar: AppBar(
backgroundColor: FlutterTheme.primaryColor,
title: Text(
'Prospectos',
style: TextStyle(fontFamily: 'Montserrat'),
),
centerTitle: true,
),
drawer: DrawerWidget(),
body: _groups == null
? LoadingWidget()
: Align(
alignment: Alignment(0, 0),
child: Padding(
padding: EdgeInsets.fromLTRB(30, 10, 30, 0),
child: ListView(
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
children: _groups == null
? []
: [
Form(
key: _formKey,
child: Row(
children: [
Expanded(
flex: 2,
child: Container(
padding:
EdgeInsets.symmetric(horizontal: 15),
child: InputWidget(
label: 'Buscar prospecto?',
keyBoardType: TextInputType.text,
onChanged: (text) {
_search = text;
},
validator: (text) {
if (text.trim().length == 0) {
return "input data";
}
return null;
},
),
),
),
Expanded(
child: ElevatedButton(
child: Text(
'Buscar',
style: TextStyle(
color: FlutterTheme.primaryColor),
),
onPressed: () {
getLeads(_search);
},
style: ElevatedButton.styleFrom(
side: BorderSide(
width: 1.5,
color: FlutterTheme.primaryColor,
),
primary: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
),
),
),
],
),
),
phoneCall.isComplete
? Container(
child: Text('The call is end!!'),
)
: Container(),
for (var lead in _leads ?? [])
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Slidable(
actionPane: SlidableDrawerActionPane(),
actionExtentRatio: 0.25,
child: Container(
color: Colors.white,
child: ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(
'https://ui-avatars.com/api/?name=' +
lead.name +
'&background=35126A&color=fff&size=128'),
),
title: Text(lead.name),
subtitle: Text(
'Grupo: ' + lead.group.name,
style: TextStyle(fontSize: 11),
),
trailing: Container(
width: 100,
child: Row(
children: [
IconButton(
onPressed: () {
_initCall(lead.phone);
},
icon: Icon(
Icons.phone,
size: 35,
color: Color(0xFF35126A),
)),
IconButton(
onPressed: () {
showCallInfo();
},
icon: Icon(
Icons.mail_outline,
size: 35,
color: Color(0xFF35126A),
)),
],
),
),
),
),
secondaryActions: <Widget>[
IconSlideAction(
onTap: () {
Navigator.pushNamed(context, 'leads_edit',
arguments: lead);
},
caption: 'Edit',
color: Color(0xFF35126A),
icon: Icons.edit,
),
IconSlideAction(
onTap: () {
deleteModal(lead.id);
},
caption: 'Delete',
color: Colors.red,
icon: Icons.delete,
),
],
),
),
Padding(
padding: EdgeInsets.fromLTRB(0, 40, 0, 0),
child: ButtonWidget(
onPressed: () {
Navigator.pushNamed(context, 'leads_add_new');
},
text: 'Agregar Nuevo',
options: ButtonOptions(
width: 130,
height: 40,
color: FlutterTheme.tertiaryColor,
textStyle: FlutterTheme.subtitle2.override(
fontFamily: 'Montserrat',
color: FlutterTheme.primaryColor,
),
borderSide: BorderSide(
color: FlutterTheme.primaryColor,
width: 2,
),
borderRadius: 30,
),
),
),
Padding(
padding: EdgeInsets.fromLTRB(0, 40, 0, 0),
child: Text(
'Grupos',
textAlign: TextAlign.center,
style: FlutterTheme.bodyText1.override(
fontFamily: 'Montserrat',
color: Color(0xBF35126A),
fontSize: 20,
),
),
),
for (var group in _groups)
Padding(
padding: EdgeInsets.fromLTRB(0, 20, 0, 0),
child: ButtonWidget(
onPressed: () {
Navigator.pushNamed(context, 'leads_list',
arguments: group);
},
text: group.name,
options: ButtonOptions(
width: 130,
height: 40,
color: FlutterTheme.primaryColor,
textStyle: FlutterTheme.subtitle2.override(
fontFamily: 'Montserrat',
color: Colors.white,
),
borderSide: BorderSide(
color: Colors.transparent,
width: 1,
),
borderRadius: 30,
),
),
),
],
),
),
),
);
}
}
The function _initCall is the one in charge of initializing the call, so the variable phoneCall will have the information of the state of the call, I want to launch a modal when the call finishes.
i need yur advice .
I Have code to fetch data from api to ListView .
The question is , how to create searchview in this listview .
class PM extends StatefulWidget {
#override
_PMState createState() => _PMState();
}
class _PMState extends State<PM> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
void showInSnackBar(String value) {
_scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Text(value),
backgroundColor: Colors.blueAccent,
));
}
final GlobalKey<RefreshIndicatorState> _refresh =
GlobalKey<RefreshIndicatorState>();
ModelPM modelPM;
ModelPM _modelPM;
bool loading = false;
Future<Null> _fetchData() async {
setState(() => loading = true);
var value;
SharedPreferences preferences = await SharedPreferences.getInstance();
setState(() {
value = preferences.getString("id");
});
final response = await http.post(BaseURL.systemTicket, body: {
"key": BaseURL.apiKey,
"method": "get",
"resource": "tabel_pm",
"filters[adminidtabelpm]": value,
});
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
final pmModelFromJson = ModelPM.fromJson(data);
setState(() {
modelPM = pmModelFromJson;
loading = false;
});
} else {
showInSnackBar("Data Gagal Load");
}
}
#override
void initState() {
super.initState();
_fetchData();
}
_listPM(i) {
final x = modelPM.results[i];
return Card(
elevation: 8.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: ListTile(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => DetilPM(x, _fetchData)));
},
contentPadding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
x.name,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 12,
color: x.status == "Not Yet"
? Colors.blue
: x.status == "Pending" ? Colors.red : Colors.green,
fontWeight: FontWeight.bold),
),
Text(
'Status : ' + x.status,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 12,
color: x.status == "Not Yet"
? Colors.blue
: x.status == "Pending" ? Colors.red : Colors.green,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 10,
),
],
),
subtitle: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 0.0),
child: Text("MIDTID",
style: TextStyle(color: Colors.black, fontSize: 10))),
),
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 0.0),
child: Text("TID",
style: TextStyle(color: Colors.black, fontSize: 10))),
),
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 0.0),
child: Text("CSI",
style: TextStyle(color: Colors.black, fontSize: 10))),
),
],
),
Row(
children: <Widget>[
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 0.0),
child: Text(x.midtid,
style: TextStyle(color: Colors.black, fontSize: 10))),
),
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 0.0),
child: Text(x.tid,
style: TextStyle(color: Colors.black, fontSize: 10))),
),
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 0.0),
child: Text(x.csi,
style: TextStyle(color: Colors.black, fontSize: 10))),
),
],
)
],
),
trailing:
Icon(Icons.keyboard_arrow_right, color: Colors.black, size: 30.0),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('PM CIMB List'),
),
key: _scaffoldKey,
body: RefreshIndicator(
onRefresh: _fetchData,
key: _refresh,
child: loading
? Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: modelPM.results.length,
itemBuilder: (context, i) {
return _listPM(i);
},
),
),
);
}
}
and below this model class PM .
class ModelPM {
final int status;
final String status_message;
final List<ModelPMResult> results;
ModelPM({this.status, this.status_message, this.results});
factory ModelPM.fromJson(Map<String, dynamic> json) {
List<ModelPMResult> results = (json['result'] as List)
.map((resultTicketJson) => ModelPMResult.fromJson(resultTicketJson))
.toList();
return ModelPM(
status: json['status'],
status_message: json['status_message'],
results: results,
);
}
}
class ModelPMResult {
final String id;
final String admintabelpm;
final String namaitfs;
final String serial;
final String merchantid;
final String assetid;
final String kondisi_edc;
final String status;
final String detail_edc;
final String note;
final String foto_struk;
final String foto_mesin;
final String foto_toko;
final String kondisi_merchant;
final String request_merchant;
final String tgl_pm;
final String name;
final String batch;
final String idmerchant;
final String midtid;
final String tid;
final String csi;
final String sign;
ModelPMResult({
this.id,
this.admintabelpm,
this.namaitfs,
this.serial,
this.merchantid,
this.assetid,
this.kondisi_edc,
this.status,
this.detail_edc,
this.kondisi_merchant,
this.request_merchant,
this.tgl_pm,
this.name,
this.batch,
this.idmerchant,
this.midtid,
this.tid,
this.csi,
this.foto_mesin,
this.foto_struk,
this.foto_toko,
this.note,
this.sign,
});
factory ModelPMResult.fromJson(Map<String, dynamic> json) {
return new ModelPMResult(
id: json['id'],
admintabelpm: json['id'],
namaitfs: json['namaitfs'],
serial: json['serial'],
merchantid: json['merchantid'],
assetid: json['assetid'],
kondisi_edc: json['kondisi_edc'],
status: json['status'],
detail_edc: json['detail_edc'],
kondisi_merchant: json['kondisi_merchant'],
request_merchant: json['request_merchant'],
tgl_pm: json['tgl_pm'],
name: json['name'],
batch: json['batch'],
idmerchant: json['idmerchant'],
midtid: json['midtid'],
tid: json['tid'],
csi: json['csi'],
note: json['note'],
foto_mesin: json['foto_mesin'],
foto_toko: json['foto_toko'],
foto_struk: json['foto_struk'],
sign: json['sign'],
);
}
}
Please advice for , how to create listview menu in my Page Flutter .
Thanks for your advice .
and also how after the data is deleted in the search menu, data from the API returns to the list
Two solutions : you can copy paste run full code below
Solution 1 : Search use current ListView page, In itemBuilder return data only fit your condition, such as string contains, if not return Container()
ListView.builder(
itemCount: modelPM.results.length,
itemBuilder: (context, i) {
if (myController.text == "") return _listPM(i);
if (myController.text != "" &&
modelPM.results[i].tid.contains(myController.text)) {
return _listPM(i);
} else {
return Container();
}
},
),
demo 1
full code 1
import 'package:flutter/material.dart';
import 'dart:convert';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: PM(),
);
}
}
class PM extends StatefulWidget {
#override
_PMState createState() => _PMState();
}
class _PMState extends State<PM> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
void showInSnackBar(String value) {
_scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Text(value),
backgroundColor: Colors.blueAccent,
));
}
final GlobalKey<RefreshIndicatorState> _refresh =
GlobalKey<RefreshIndicatorState>();
ModelPM modelPM;
ModelPM _modelPM;
bool loading = false;
Future<Null> _fetchData() async {
setState(() => loading = true);
var value;
/*SharedPreferences preferences = await SharedPreferences.getInstance();
setState(() {
value = preferences.getString("id");
});
final response = await http.post(BaseURL.systemTicket, body: {
"key": BaseURL.apiKey,
"method": "get",
"resource": "tabel_pm",
"filters[adminidtabelpm]": value,
});*/
/* if (response.statusCode == 200) {
final data = jsonDecode(response.body);
final pmModelFromJson = ModelPM.fromJson(data);
setState(() {
modelPM = pmModelFromJson;
loading = false;
});
} else {
showInSnackBar("Data Gagal Load");
}*/
String responsebody = '''
{
"status": 200,
"status_message" : "OK",
"result" : [
{
"id": "123",
"name" : "name1",
"notes" : "notes1",
"midtid" : "midtid1",
"tid" : "tid1",
"csi" : "csi1",
"status" : "abc"
}
,
{
"id": "456",
"name" : "name2",
"notes" : "notes2",
"midtid" : "midtid2",
"tid" : "tid2",
"csi" : "csi2",
"status" : "def"
}
]
}
''';
final data = jsonDecode(responsebody);
final pmModelFromJson = ModelPM.fromJson(data);
setState(() {
modelPM = pmModelFromJson;
loading = false;
});
}
#override
void initState() {
super.initState();
_fetchData();
}
_listPM(i) {
final x = modelPM.results[i];
return Card(
elevation: 8.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: ListTile(
onTap: () {
/*Navigator.of(context).push(
MaterialPageRoute(builder: (context) => DetilPM(x, _fetchData)));*/
},
contentPadding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
x.name,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 12,
color: x.status == "Not Yet"
? Colors.blue
: x.status == "Pending" ? Colors.red : Colors.green,
fontWeight: FontWeight.bold),
),
Text(
'Status : ' + x.status,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 12,
color: x.status == "Not Yet"
? Colors.blue
: x.status == "Pending" ? Colors.red : Colors.green,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 10,
),
],
),
subtitle: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 0.0),
child: Text("MIDTID",
style: TextStyle(color: Colors.black, fontSize: 10))),
),
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 0.0),
child: Text("TID",
style: TextStyle(color: Colors.black, fontSize: 10))),
),
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 0.0),
child: Text("CSI",
style: TextStyle(color: Colors.black, fontSize: 10))),
),
],
),
Row(
children: <Widget>[
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 0.0),
child: Text(x.midtid,
style: TextStyle(color: Colors.black, fontSize: 10))),
),
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 0.0),
child: Text(x.tid,
style: TextStyle(color: Colors.black, fontSize: 10))),
),
Expanded(
flex: 4,
child: Padding(
padding: EdgeInsets.only(left: 0.0),
child: Text(x.csi,
style: TextStyle(color: Colors.black, fontSize: 10))),
),
],
)
],
),
trailing:
Icon(Icons.keyboard_arrow_right, color: Colors.black, size: 30.0),
),
);
}
final myController = TextEditingController();
#override
void dispose() {
// Clean up the controller when the widget is removed from the
// widget tree.
myController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: TextField(
controller: myController,
decoration:
InputDecoration(border: InputBorder.none, hintText: 'Search'),
onChanged: (value) {
setState(() {});
},
),
),
key: _scaffoldKey,
body: RefreshIndicator(
onRefresh: _fetchData,
key: _refresh,
child: loading
? Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: modelPM.results.length,
itemBuilder: (context, i) {
if (myController.text == "") return _listPM(i);
if (myController.text != "" &&
modelPM.results[i].tid.contains(myController.text)) {
return _listPM(i);
} else {
return Container();
}
},
),
),
);
}
}
class ModelPM {
final int status;
final String status_message;
final List<ModelPMResult> results;
ModelPM({this.status, this.status_message, this.results});
factory ModelPM.fromJson(Map<String, dynamic> json) {
List<ModelPMResult> results = (json['result'] as List)
.map((resultTicketJson) => ModelPMResult.fromJson(resultTicketJson))
.toList();
return ModelPM(
status: json['status'],
status_message: json['status_message'],
results: results,
);
}
}
class ModelPMResult {
final String id;
final String admintabelpm;
final String namaitfs;
final String serial;
final String merchantid;
final String assetid;
final String kondisi_edc;
final String status;
final String detail_edc;
final String note;
final String foto_struk;
final String foto_mesin;
final String foto_toko;
final String kondisi_merchant;
final String request_merchant;
final String tgl_pm;
final String name;
final String batch;
final String idmerchant;
final String midtid;
final String tid;
final String csi;
final String sign;
ModelPMResult({
this.id,
this.admintabelpm,
this.namaitfs,
this.serial,
this.merchantid,
this.assetid,
this.kondisi_edc,
this.status,
this.detail_edc,
this.kondisi_merchant,
this.request_merchant,
this.tgl_pm,
this.name,
this.batch,
this.idmerchant,
this.midtid,
this.tid,
this.csi,
this.foto_mesin,
this.foto_struk,
this.foto_toko,
this.note,
this.sign,
});
factory ModelPMResult.fromJson(Map<String, dynamic> json) {
return new ModelPMResult(
id: json['id'],
admintabelpm: json['id'],
namaitfs: json['namaitfs'],
serial: json['serial'],
merchantid: json['merchantid'],
assetid: json['assetid'],
kondisi_edc: json['kondisi_edc'],
status: json['status'],
detail_edc: json['detail_edc'],
kondisi_merchant: json['kondisi_merchant'],
request_merchant: json['request_merchant'],
tgl_pm: json['tgl_pm'],
name: json['name'],
batch: json['batch'],
idmerchant: json['idmerchant'],
midtid: json['midtid'],
tid: json['tid'],
csi: json['csi'],
note: json['note'],
foto_mesin: json['foto_mesin'],
foto_toko: json['foto_toko'],
foto_struk: json['foto_struk'],
sign: json['sign'],
);
}
}
Solution 2 : Search with SearchDelegate, data actually display in another page
When click search button, open another page
demo
full demo code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: SearchDemo(),
);
}
}
class SearchDemo extends StatefulWidget {
static const String routeName = '/material/search';
#override
_SearchDemoState createState() => _SearchDemoState();
}
class _SearchDemoState extends State<SearchDemo> {
final _SearchDemoSearchDelegate _delegate = _SearchDemoSearchDelegate();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
int _lastIntegerSelected;
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
leading: IconButton(
tooltip: 'Navigation menu',
icon: AnimatedIcon(
icon: AnimatedIcons.menu_arrow,
color: Colors.white,
progress: _delegate.transitionAnimation,
),
onPressed: () {
_scaffoldKey.currentState.openDrawer();
},
),
title: const Text('Numbers'),
actions: <Widget>[
IconButton(
tooltip: 'Search',
icon: const Icon(Icons.search),
onPressed: () async {
final int selected = await showSearch<int>(
context: context,
delegate: _delegate,
);
if (selected != null && selected != _lastIntegerSelected) {
setState(() {
_lastIntegerSelected = selected;
});
}
},
),
//MaterialDemoDocumentationButton(SearchDemo.routeName),
IconButton(
tooltip: 'More (not implemented)',
icon: Icon(
Theme.of(context).platform == TargetPlatform.iOS
? Icons.more_horiz
: Icons.more_vert,
),
onPressed: () { },
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
MergeSemantics(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text('Press the '),
Tooltip(
message: 'search',
child: Icon(
Icons.search,
size: 18.0,
),
),
Text(' icon in the AppBar'),
],
),
const Text('and search for an integer between 0 and 100,000.'),
],
),
),
const SizedBox(height: 64.0),
Text('Last selected integer: ${_lastIntegerSelected ?? 'NONE' }.'),
],
),
),
floatingActionButton: FloatingActionButton.extended(
tooltip: 'Back', // Tests depend on this label to exit the demo.
onPressed: () {
Navigator.of(context).pop();
},
label: const Text('Close demo'),
icon: const Icon(Icons.close),
),
drawer: Drawer(
child: Column(
children: <Widget>[
const UserAccountsDrawerHeader(
accountName: Text('Peter Widget'),
accountEmail: Text('peter.widget#example.com'),
currentAccountPicture: CircleAvatar(
backgroundImage: AssetImage(
'people/square/peter.png',
package: 'flutter_gallery_assets',
),
),
margin: EdgeInsets.zero,
),
MediaQuery.removePadding(
context: context,
// DrawerHeader consumes top MediaQuery padding.
removeTop: true,
child: const ListTile(
leading: Icon(Icons.payment),
title: Text('Placeholder'),
),
),
],
),
),
);
}
}
class _SearchDemoSearchDelegate extends SearchDelegate<int> {
final List<int> _data = List<int>.generate(100001, (int i) => i).reversed.toList();
final List<int> _history = <int>[42607, 85604, 66374, 44, 174];
#override
Widget buildLeading(BuildContext context) {
return IconButton(
tooltip: 'Back',
icon: AnimatedIcon(
icon: AnimatedIcons.menu_arrow,
progress: transitionAnimation,
),
onPressed: () {
close(context, null);
},
);
}
#override
Widget buildSuggestions(BuildContext context) {
final Iterable<int> suggestions = query.isEmpty
? _history
: _data.where((int i) => '$i'.startsWith(query));
return _SuggestionList(
query: query,
suggestions: suggestions.map<String>((int i) => '$i').toList(),
onSelected: (String suggestion) {
query = suggestion;
showResults(context);
},
);
}
#override
Widget buildResults(BuildContext context) {
final int searched = int.tryParse(query);
if (searched == null || !_data.contains(searched)) {
return Center(
child: Text(
'"$query"\n is not a valid integer between 0 and 100,000.\nTry again.',
textAlign: TextAlign.center,
),
);
}
return ListView(
children: <Widget>[
_ResultCard(
title: 'This integer',
integer: searched,
searchDelegate: this,
),
_ResultCard(
title: 'Next integer',
integer: searched + 1,
searchDelegate: this,
),
_ResultCard(
title: 'Previous integer',
integer: searched - 1,
searchDelegate: this,
),
],
);
}
#override
List<Widget> buildActions(BuildContext context) {
return <Widget>[
if (query.isEmpty)
IconButton(
tooltip: 'Voice Search',
icon: const Icon(Icons.mic),
onPressed: () {
query = 'TODO: implement voice input';
},
)
else
IconButton(
tooltip: 'Clear',
icon: const Icon(Icons.clear),
onPressed: () {
query = '';
showSuggestions(context);
},
),
];
}
}
class _ResultCard extends StatelessWidget {
const _ResultCard({this.integer, this.title, this.searchDelegate});
final int integer;
final String title;
final SearchDelegate<int> searchDelegate;
#override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
return GestureDetector(
onTap: () {
searchDelegate.close(context, integer);
},
child: Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
Text(title),
Text(
'$integer',
style: theme.textTheme.headline.copyWith(fontSize: 72.0),
),
],
),
),
),
);
}
}
class _SuggestionList extends StatelessWidget {
const _SuggestionList({this.suggestions, this.query, this.onSelected});
final List<String> suggestions;
final String query;
final ValueChanged<String> onSelected;
#override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
return ListView.builder(
itemCount: suggestions.length,
itemBuilder: (BuildContext context, int i) {
final String suggestion = suggestions[i];
return ListTile(
leading: query.isEmpty ? const Icon(Icons.history) : const Icon(null),
title: RichText(
text: TextSpan(
text: suggestion.substring(0, query.length),
style: theme.textTheme.subhead.copyWith(fontWeight: FontWeight.bold),
children: <TextSpan>[
TextSpan(
text: suggestion.substring(query.length),
style: theme.textTheme.subhead,
),
],
),
),
onTap: () {
onSelected(suggestion);
},
);
},
);
}
}
I'm trying to implement the API found here into my home_page.dart found below using this flutter learners project for JSON parsing. You can find the full code at this GitHub Repo
Looking at the current picture below, I would like the end project 'Trending News' section to return the article picture from the API, as well as the title instead of 'Item 1', 'Item 2' etc...
I'm having issues with this version, as when I try to run for debug I get the issue; The getter length was called on null
home_page.dart;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:one/cryptoData/crypto_data.dart';
import 'package:one/modules/crypto_presenter.dart';
import 'main.dart';
import 'dart:convert';
import 'background.dart';
import 'dart:async';
import 'package:http/http.dart' as http;
Future<List<Photo>> fetchPhotos(http.Client client) async {
final response =
await client.get('https://newsapi.org/v2/top-headlines?sources=crypto-coins-news&apiKey=d40a757cfb2e4dd99fc511a0cbf59098');
// Use the compute function to run parsePhotos in a separate isolate
return compute(parsePhotos, response.body);
}
// A function that will convert a response body into a List<Photo>
List<Photo> parsePhotos(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();
}
class Photo {
final int albumId;
final int id;
final String title;
final String url;
final String thumbnailUrl;
Photo({this.albumId, this.id, this.title, this.url, this.thumbnailUrl});
factory Photo.fromJson(Map<String, dynamic> json) {
return Photo(
albumId: json['albumId'] as int,
id: json['id'] as int,
title: json['title'] as String,
url: json['url'] as String,
thumbnailUrl: json['thumbnailUrl'] as String,
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> implements CryptoListViewContract {
CryptoListPresenter _presenter;
List<Crypto> _currencies;
bool _isLoading;
final List<MaterialColor> _colors = [Colors.blue, Colors.indigo, Colors.red];
final List<Photo> photos;
_HomePageState() {
_presenter = new CryptoListPresenter(this);
}
List<String> items = [
"Item 1",
"Item 2",
"Item 3",
"Item 4",
"Item 5",
"Item 6",
"Item 7",
"Item 8"
];
#override
void initState() {
super.initState();
_isLoading = true;
_presenter.loadCurrencies();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Crypto App",
style: new TextStyle(
color: Colors.white,
fontFamily: 'Poppins',
fontSize: 22.5,
),
),
backgroundColor: const Color(0xFF273A48),
elevation: 0.0,
centerTitle: true,
),
body: _isLoading
? new Center(
child: new CircularProgressIndicator(),
)
: _allWidget()
);
}
Widget _allWidget() {
final _width = MediaQuery.of(context).size.width;
final _height = MediaQuery.of(context).size.height;
//CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED
final headerList = new ListView.builder(
itemBuilder: (context, index) {
EdgeInsets padding = index == 0?const EdgeInsets.only(
left: 20.0, right: 10.0, top: 4.0, bottom: 30.0):const EdgeInsets.only(
left: 10.0, right: 10.0, top: 4.0, bottom: 30.0);
return new Padding(
padding: padding,
child: new InkWell(
onTap: () {
print('Card selected');
},
child: new Container(
decoration: new BoxDecoration(
borderRadius: new BorderRadius.circular(10.0),
color: Colors.lightGreen,
boxShadow: [
new BoxShadow(
color: Colors.black.withAlpha(70),
offset: const Offset(3.0, 10.0),
blurRadius: 15.0)
],
image: new DecorationImage(
image: new ExactAssetImage(
'assets/img_${index%items.length}.jpg'),
fit: BoxFit.fitHeight,
),
),
// height: 200.0,
width: 200.0,
child: new Stack(
children: <Widget>[
new Align(
alignment: Alignment.bottomCenter,
child: new Container(
decoration: new BoxDecoration(
color: const Color(0xFF273A48),
borderRadius: new BorderRadius.only(
bottomLeft: new Radius.circular(10.0),
bottomRight: new Radius.circular(10.0))),
height: 30.0,
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'${items[index%items.length]}',
style: new TextStyle(color: Colors.white),
)
],
)),
)
],
),
),
),
);
},
scrollDirection: Axis.horizontal,
itemCount: photos.length,
);
final body = new Scaffold(
backgroundColor: Colors.transparent,
body: new Container(
child: new Stack(
children: <Widget>[
new Padding(
padding: new EdgeInsets.only(top: 10.0),
child: new Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Align(
alignment: Alignment.centerLeft,
child: new Padding(
padding: new EdgeInsets.only(left: 10.0,),
child: new Text(
"Trending News",
style: new TextStyle(
letterSpacing: 0.8,
fontFamily: 'Kanit',
fontSize: 17.5,
color: Colors.white,
),
)
),
),
new Container(
height: 300.0, width: _width, child: headerList),
new Expanded(child:
ListView.builder(
itemBuilder: (BuildContext context, int index) {
final int i = index;
final Crypto currency = _currencies[i];
final MaterialColor color = _colors[i % _colors.length];
return new ListTile(
title: new Column(
children: <Widget>[
new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Container(
height: 72.0,
width: 72.0,
decoration: new BoxDecoration(
color: Colors.white,
boxShadow: [
new BoxShadow(
color:
Colors.black.withAlpha(80),
offset: const Offset(2.0, 2.0),
blurRadius: 15.0)
],
borderRadius: new BorderRadius.all(
new Radius.circular(35.0)),
image: new DecorationImage(
image: new ExactAssetImage(
"cryptoiconsBlack/"+currency.symbol.toLowerCase()+"#2x.png",
),
fit: BoxFit.cover,
)),
),
new SizedBox(
width: 8.0,
),
new Expanded(
child: new Column(
mainAxisAlignment:
MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
new Text(
currency.name,
style: new TextStyle(
fontSize: 14.0,
fontFamily: 'Poppins',
color: Colors.black87,
fontWeight: FontWeight.bold),
),
_getSubtitleText(currency.price_usd, currency.percent_change_1h),
],
)),
],
),
new Divider(),
],
),
);
}))
],
),
),
],
),
),
);
return new Container(
decoration: new BoxDecoration(
color: const Color(0xFF273A48),
),
child: new Stack(
children: <Widget>[
new CustomPaint(
size: new Size(_width, _height),
painter: new Background(),
),
body,
],
),
);
}
// CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED CRYPTO FEED
Widget _getSubtitleText(String priceUSD, String percentageChange) {
TextSpan priceTextWidget = new TextSpan(
text: "\$$priceUSD\n", style: new TextStyle(color: Colors.black));
String percentageChangeText = "1 hour: $percentageChange%";
TextSpan percentageChangeTextWidget;
if (double.parse(percentageChange) > 0) {
percentageChangeTextWidget = new TextSpan(
text: percentageChangeText,
style: new TextStyle(color: Colors.green));
} else {
percentageChangeTextWidget = new TextSpan(
text: percentageChangeText, style: new TextStyle(color: Colors.red));
}
return new RichText(
text: new TextSpan(
children: [priceTextWidget, percentageChangeTextWidget]));
}
//DONT TOUCH, Works with cryptoListViewContract implimentation in _MyHomePageState
#override
void onLoadCryptoComplete(List<Crypto> items) {
// TODO: implement onLoadCryptoComplete
setState(() {
_currencies = items;
_isLoading = false;
});
}
#override
void onLoadCryptoError() {
// TODO: implement onLoadCryptoError
}
}
Last working version of the app;
Thanks, Jake
The list photos is null in your case. It is not initialized anywhere. I think you forgot to call fetchPhotos() method which returns List<Photo>. That's why null pointer exception occured.
Try changing everywhere where you have
.length
to
(... ?.length ?? 0)
Like
itemCount: photos?.length ?? 0,
final MaterialColor color = _colors[i % (_colors?.length ?? 0)];
'assets/img_${index % (items?.length ?? 0)}.jpg'),
...
replace
class Photo {
final int albumId;
final int id;
final String title;
final String url;
final String thumbnailUrl;
Photo({this.albumId, this.id, this.title, this.url, this.thumbnailUrl});
factory Photo.fromJson(Map<String, dynamic> json) {
return Photo(
albumId: json['albumId'] as int,
id: json['id'] as int,
title: json['title'] as String,
url: json['url'] as String,
thumbnailUrl: json['thumbnailUrl'] as String,
);
}
}
with:
class Photo {
final String albumId;
final String id;
final String title;
final String url;
final String thumbnailUrl;
Photo({this.albumId, this.id, this.title, this.url, this.thumbnailUrl});
factory Photo.fromJson(Map<String, dynamic> json) {
return Photo(
albumId: json['albumId'] as String,
id: json['id'] as String,
title: json['title'] as String,
url: json['url'] as String,
thumbnailUrl: json['thumbnailUrl'] as String,
);
}
}