How to show Event on Table Calendar using my API on flutter - json

I have UI for show event calendars and I need to show events from my API. but I don't know how to do it. I try to change List on _event but there's no response. And I need to show it on the calendar so my company calendar can show the event.
this my UI Calendar
import 'package:intl/intl.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:urus_flutter/persentation/custom_color.dart';
import 'package:urus_flutter/persentation/custom_text_style.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Calender extends StatefulWidget {
#override
State<StatefulWidget> createState() => CalenderState();
}
class CalenderState extends State<Calender> {
CalendarController _controller;
Map<DateTime, List<dynamic>> _events;
List<dynamic> _selectedEvents;
DateTime _selectedDate;
SharedPreferences prefs;
#override
void initState(){
super.initState();
_controller = CalendarController();
_events = {
DateTime(2021, 6, 22) : ['Meeting URUS', 'Testing Danai Mobile', 'Weekly Report', 'Weekly Meeting'],
DateTime(2021, 6, 25) : ['Weekly Testing'],
DateTime(2021, 6, 4) : ['Weekly Testing'],
DateTime(2021, 6, 11) : ['Weekly Testing'],
DateTime(2021, 6, 18) : ['Weekly Testing'],
};
}
Map<String, dynamic> encodeMap(Map<DateTime, dynamic> map) {
Map<String, dynamic> newMap = {};
map.forEach((key, value) {
newMap[key.toString()] = map[key];
});
return newMap;
}
Map<DateTime, dynamic> decodeMap(Map<String, dynamic> map) {
Map<DateTime, dynamic> newMap = {};
map.forEach((key, value) {
newMap[DateTime.parse(key)] = map[key];
});
return newMap;
}
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
ListView(
padding: EdgeInsets.only(left: 16, right: 16, top: 52, bottom: 126),
children: [
Text("Kalender Kegiatan",
style: CustomTextStlye.proxima_bold_18_black,),
Container(
child: Padding(
padding: const EdgeInsets.only(top: 28.0),
child: Text("Kalender Anda",
style: CustomTextStlye.proxima_bold_16_black,),
),
),
SizedBox(
height: 20,
),
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5.0),
boxShadow: [
BoxShadow(
offset: Offset(0, -1),
color: CustomColor.border_grey,
blurRadius: 3.0,
spreadRadius: 1.0)
]
),
child: TableCalendar(
initialCalendarFormat: CalendarFormat.month,
calendarStyle: CalendarStyle(
todayColor: Color(0x9429AAE1),
todayStyle: CustomTextStlye.proxima_bold_12_white,
selectedColor: Color(0xFF29AAE1),
selectedStyle: CustomTextStlye.proxima_bold_12_white,
weekdayStyle: CustomTextStlye.proxima_bold_12_black,
weekendStyle: CustomTextStlye.proxima_bold_12_red,
unavailableStyle: CustomTextStlye.proxima_bold_12,
holidayStyle: CustomTextStlye.proxima_bold_12_red,
markersColor: Color(0xFFA2CD3A),
),
headerStyle: HeaderStyle(
centerHeaderTitle: true,
formatButtonVisible: false,
titleTextStyle: CustomTextStlye.proxima_bold_14_black,
),
availableCalendarFormats: const {CalendarFormat.month: '',},
startingDayOfWeek: StartingDayOfWeek.monday,
calendarController: _controller,
events: _events,
onDaySelected: (date, events,holidays) {
setState(() {
_selectedEvents = events;
_selectedDate = date;
});
},
),
)
],
),
),
Container(
child:Padding(
padding: const EdgeInsets.only(top: 28.0),
child: Text("Kegiatan Anda",
style: CustomTextStlye.proxima_bold_16_black,),
),
),
Container(
child: _selectedEvents != null ? Column(
children: List.generate(_selectedEvents.length, (index) =>
Container(
padding: const EdgeInsets.all(8.0),
child: Container(
height: MediaQuery.of(context).size.height/15,
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: Color.fromRGBO(228, 228, 228, 1)))
),
child:
Center(
child:
Container(child:
Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 10.0),
height: MediaQuery.of(context).size.height/10,
decoration: BoxDecoration(
border: Border.all(color:Color(0xFF29AAE1)),
color:Color(0xFF29AAE1),
borderRadius: BorderRadius.circular(3.0),
),
child: Text(DateFormat('d').format(_selectedDate),
style: CustomTextStlye.proxima_bold_18_white,
),
),
),
Text(_selectedEvents[index],
style: CustomTextStlye.proxima_bold_14_black,
),
],
),
)
),
),
),
),
) : Container(),
)
],
),
],
),
);
}
}
Here my eventCalendarService.dart
import 'dart:convert';
import 'dart:io';
import 'package:http/io_client.dart';
import 'package:urus_flutter/data/eventController.dart';
import 'package:urus_flutter/data/model/base/event_calendar.dart';
Future<List<Event_Calendar>> fetchEventCalendar(String id, int company_id, {String date}) async {
String requestBody = '';
print(requestBody);
final ioc = new HttpClient();
ioc.badCertificateCallback = (X509Certificate cert, String host, int port) => true;
final http = new IOClient(ioc);
final response = await http.get(
getStringUrl+'staffs/GetCalendar?companyid=$company_id&month=$date',
);
print(response.statusCode);
print(response.body);
if (response.statusCode == 200) {
var parsed = jsonDecode(response.body);
return List<Event_Calendar>.from(parsed.map((model) => Event_Calendar.fromJson(model)));
} else {
throw Exception(response.body);
}
}
And here my model event_calendar.dart
class Event_Calendar {
final String id;
final String type;
final String date;
final String event_name;
final int company_id;
Event_Calendar(
{
this.id,
this.type,
this.date,
this.event_name,
this.company_id,
}
);
factory Event_Calendar.fromJson(Map<String, dynamic> json) {
return Event_Calendar(
id: json['id'] as String,
type: json['type'] as String,
date: json['date'] as String,
event_name: json['event_name'] as String,
company_id: json['company_id'] as int,
);
}
}
I hope anyone can give me an answer on how to show _events to my API. Thank you. And this is the example value from my API

Well folks now I'm going to show you the way I found to show events and cards coming from an api using the table calendar.
I don't even need to say that this is the way I found things, feel free to add new things and give tips here. so let's go.
first of all we will show the calendar events, but in this step only the markers, your data coming from the api must contain dates if you are here, in my case the dates come as a String, so let's create the model for them
import 'dart:convert';
class EventsModel {
final String dataDoJob;
EventsModel({
required this.dataDoJob,
});
Map<String, dynamic> toMap() {
return {
'data_acao': dataDoJob,
};
}
factory EventsModel.fromMap(Map<String, dynamic> map) {
return EventsModel(
dataDoJob: map['data_acao'],
);
}
String toJson() => json.encode(toMap());
factory EventsModel.fromJson(String source) => EventsModel.fromMap(json.decode(source));
}
this is my model, as you can see I'm just getting the dates. Now let's do the get method to retrieve this data from the api, I'm using getConnect but you can use the http client you want.
#override
Future<List<EventsModel>> getEvents() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
final int? id = sharedPreferences.getInt("idInfluencer");
final String token = sharedPreferences.getString("token") ?? "";
final Response result = await _restClient.get<List<EventsModel>>(
"/job_acoes?influenciador_id=${id.toString()}",
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token'
}, decoder: (data) {
if (data != null) {
return data
.map<EventsModel>((event) => EventsModel.fromMap(event))
.toList();
}
return <EventsModel>[];
});
if (result.hasError) {
print(result.statusCode);
throw "Erro ao buscar dados";
}
print(result.body);
print(result.statusCode);
return result.body;
}
Well done that we already have a list of dates in hand, but in my case they are strings so I'll have to convert them, like this:
final events = await _jobsServices.getEvents();
//final dateFormat = DateFormat("yyyy-MM-dd");
final eventsConvert =
events.map((date) => (DateTime.parse(date.dataDoJob))).toList();
eventsList.assignAll(eventsConvert);
print("Lista de eventos : $eventsList");
streamController.add(events);
on the first line I'm saving the list in a variable called events and right below I'm converting the strings into date time using the map method, And adding them to an empty list which I created then step by step and this: Create an empty list and add the converted data to it as I did above, my empty list is called eventsList
done that we will show this list in the table calendar
class CalendarWidget extends GetView<HomeController> {
const CalendarWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder<List<EventsModel>>(
stream: controller.streamController.stream,
builder: (context, snapshot) {
return Obx(() {
return TableCalendar(
eventLoader: (day) => controller.eventsList.where((event) => isSameDay(event,day)).toList(), //THIS IS IMPORTANT
focusedDay: controller.focusedDay.value,
firstDay: DateTime(2019),
lastDay: DateTime(2050),
headerStyle:
const HeaderStyle(formatButtonVisible: false), //WEEK VISIBLE
locale: 'pt_BR',
daysOfWeekVisible: true,
calendarFormat: controller.format.value,
onFormatChanged: (CalendarFormat _format) =>
controller.calendarFormat(_format),
onDaySelected: (DateTime userSelectedDay, DateTime focusedDay) =>
controller.selectedDay(userSelectedDay, focusedDay),
calendarStyle: CalendarStyle(
selectedTextStyle: const TextStyle(color: Colors.white),
isTodayHighlighted: true,
selectedDecoration: BoxDecoration(
color: context.buttomThemeClicled,
shape: BoxShape.circle)),
selectedDayPredicate: (DateTime date) {
return isSameDay(controller.focusedDay.value, date);
},
);
});
});
}
}
remembering that i'm using a stateless widget so i need a state manager, and i use getx so it has an obx involving the whole calendar.
in the event loader some people have doubts, you can pass a list or a map on it, in my case I'm working with a list for simplicity's sake, notice that in the event loader I'm doing a simple filtering, comparing the dates of the my calendar with the dates I get from the api, simple isn't it? by doing this your api-based bookmarks will already be shown. Ah, a detail, the stream builder serves to redo my widget whenever there are changes in the api, if you don't know how to work with it, this video will explain: https://www.youtube.com/watch?v=BBelgajHgzY
now let's go to the display part of your events based on days, my events will be displayed on cards like this:
so I built it on a separate page from my home, this part is important because your code will be easier and cleaner, with the widget done, we'll show them on screen like this:
child: Obx(() {
return ListView(
scrollDirection: Axis.vertical,
children: controller.cards
.map(
(c) => AgendaCards(
bottomPosition: 80,
leftPositioned: 260,
maxRadius: 5,
rightPositioned: 5,
secondMaxradius: 5,
topPositioned: 20,
model: c,
),
)
.toList(),
);
}));
the widget called calendar cards is nothing more than the card in the photo above, on it I asked for a model called
final JobsDescriptionCardsModel model;
and called him in the constructor
AgendaCards({
required this.leftPositioned,
required this.rightPositioned,
required this.topPositioned,
required this.bottomPosition,
required this.maxRadius,
required this.secondMaxradius,
required this.model, //HERE
Key? key,
}) : super(key: key);
so let's create this model
class JobsDescriptionCardsModel {
final String descricaoJob;
final String dataDoJob;
final String horarioDoJob;
final int jobId;
final String nome;
JobsDescriptionCardsModel({
required this.descricaoJob,
required this.dataDoJob,
required this.horarioDoJob,
required this.jobId,
required this.nome,
});
Map<String, dynamic> toMap() {
return {
'descricaoJob': descricaoJob,
'dataDoJob': dataDoJob,
'horarioDoJob': horarioDoJob,
'jobId': jobId,
'nome': nome,
};
}
factory JobsDescriptionCardsModel.fromMap(Map<String, dynamic> map) {
return JobsDescriptionCardsModel(
descricaoJob: map['descricao'] ?? "",
dataDoJob: map['data_acao'] ?? "",
horarioDoJob: map['hora_inicial_acao'],
jobId: map['job_acao_id'] ?? 0,
nome: map['job'] ["cliente"] ["nome"] ?? "",
);
}
String toJson() => json.encode(toMap());
factory JobsDescriptionCardsModel.fromJson(String source) => JobsDescriptionCardsModel.fromMap(json.decode(source));
}
and get it on the api
#override
Future<List<JobsDescriptionCardsModel>> getJobsDescrition() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
final int? id = sharedPreferences.getInt("idInfluencer");
final String token = sharedPreferences.getString("token") ?? "";
final result = await _restClient.get<List<JobsDescriptionCardsModel>>(
"/job_acoes?influenciador_id=${id.toString()}",
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token'
}, decoder: (data) {
if (data != null) {
return data
.map<JobsDescriptionCardsModel>(
(j) => JobsDescriptionCardsModel.fromMap(j))
.toList();
}
return <JobsDescriptionCardsModel>[];
});
if (result.hasError) {
throw ("erro ao buscar dados");
}
return result.body ?? <JobsDescriptionCardsModel>[];
}
the api gives me a list that is important. With the list in hand, we're going to get into the concepts of the table calendar. To proceed and understand what will be done, I recommend that you watch this video: https://www.youtube.com/watch?v=HKuzPQUV21Y&t=291s
having done the configuration of your calendar, I believe you have noticed that when you click on a date and print the variable that has the date data, you will notice that the table calendar gives you a date time as a return, and if you have rendered it the attention in my model i also have a date coming from the api, knowing that we only need to filter the list coming from the api based on the table calendar data like this:
first create an empty list like we did before and a list that will be filtered:
//LISTA DE JOBS CARDS
final cards = <JobsDescriptionCardsModel>[].obs;
//LISTA FILTRADA
var cardsFiltered = <JobsDescriptionCardsModel>[];
the empty list will be filled with the api data like this:
final jobsCards = await _jobsServices.getJobsDescrition();
cards.assignAll(jobsCards);
and with the populated list in hand we will filter this list based on the api dates, like this:
cardsFiltered = jobsCards;
var novaLista = cardsFiltered.where((model) {
return model.dataDoJob
.toString()
.contains(focusedDay.value.toString().substring(1, 10));
});
see that first I assign my populated list to a new list, then I filtered this list based on my model only in the part that contains the date that I stringed, comparing with my variable that has the date when I click on it remembers ? also converted to string, as the table calendar gives me the date along with other numbers that I believe are time information I got the data only from index 1 to 10 which will give me exactly the date contained in the variable. With that done, the table calendar has a property called onDaySelected, and it will display our filtered list like this:
selectedDay(DateTime selectedDayInfo, DateTime focusDayInfo) {
userSelectedDay.value = selectedDayInfo;
focusedDay.value = focusDayInfo;
print(userSelectedDay.value);
print(focusedDay.value);
print("Lista de eventos 2 ${eventsList}");
var novaLista = cardsFiltered.where((model) {
return model.dataDoJob
.toString()
.contains(focusedDay.value.toString().substring(0, 10));
});
cards.assignAll(novaLista);
I created this separate function in a controller and called it in table calendar like this:
onDaySelected: (DateTime userSelectedDay, DateTime focusedDay) =>
controller.selectedDay(userSelectedDay, focusedDay),
done that your calendar will already be displaying the default markers and your cards based on the widget you built, remember to pass the data to your cards using the model you requested via the constructor because it contains the api data. I hope I have help and I'm sorry for the google translator english

_event was not used in the newer version of table_calendar. you can use eventLoader it will display your events on the calendar.
Example:
calendar widget as follows,
TableCalendar<Event>(
firstDay: kFirstDay,
lastDay: kLastDay,
focusedDay: _focusedDay,
selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
eventLoader: _getEventsForDay, // this will load events on calendar
),
_getEventsForDay method as follows,
List<Event> _getEventsForDay(DateTime day) {
return kEvents[day] ?? [];
}
kEvents (event list which you need to mark on the calendar) is as follows, you don't need to create this one if you have a list of events.
final kEvents = LinkedHashMap<DateTime, List<Event>>(
equals: isSameDay,
hashCode: getHashCode,
)..addAll(_kEventSource);
final _kEventSource = Map.fromIterable(List.generate(50, (index) => index),
key: (item) => DateTime.utc(kFirstDay.year, kFirstDay.month, item * 5),
value: (item) => List.generate(
item % 4 + 1, (index) => Event('Event $item | ${index + 1}')))
..addAll({
kToday: [
Event('Event 1'),
Event('Event 2'),
],
});
Event class as follows, (as well you can make it as you prefer)
class Event {
final String title;
const Event(this.title);
#override
String toString() => title;
}
Hope you got it!

Related

how map "convert" Json in flutter?

i'm new to flutter i have 2 questions :
how to access the Data in this Json for example get the name of the phone in the 2 list
how to use this Json to build a ListView using the Builder method, i looked and found out that i have to use kind of some library .. can't i do it manually ?
var mblist = [{
'name': 'Note 10',
'camera': '48 MPX',
'ram': '8GB',
'price': '20 000,00 DZD',
'image':
'https://images.frandroid.com/wp-content/uploads/2019/06/samsung-galaxy-note-10-2019-frandroid.png'
}
,
{
'name': 'Note 20',
'camera': '48 MPX',
'ram': '8GB',
'price': '20 000,00 DZD',
'image':
'https://images.frandroid.com/wp-content/uploads/2019/06/samsung-galaxy-note-10-2019-frandroid.png'
}
];
Question1: how to get the name of the phone?
name of the first phone: mblist[0]['name']
list of names of all phones: mblist.map((phone) => phone['name']).toList()
Question2: how to use json list to build a ListView?
The best way to work with data in widgets is usually to use class based model objects. You can create a class model an instantiate an object of it from the json data and pass that object around in your flutter widgets to create any kind of UI you need.
For example the model for that json list you mentioned can be like this:
class Model {
String? name;
String? camera;
String? ram;
String? price;
String? image;
Model({this.name, this.camera, this.ram, this.price, this.image});
Model.fromJson(Map<String, dynamic> json) {
name = json['name'];
camera = json['camera'];
ram = json['ram'];
price = json['price'];
image = json['image'];
}
}
You can convert your list to a list of Model objects like this:
List<Model> modelMbList = mblist.map((mb) => Model.fromJson(mb)).toList();
After that you can create your ListView like this:
ListView.builder(
itemCount: modelMbList.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(modelMbList[index].name??''),
);
},
)
In flutter there to packages json_serializable and build_runner which help you to create dart model for a Api call and other json objects.
This will reduce ton of boilerplate code for your project.
https://pub.dev/packages/json_serializable
https://pub.dev/packages/build_runner
#Mahdi's answer is correct but here's a complete example with all parameters in the UI.
class Phone {
final String name;
final String camera;
final String ram;
final String price;
final String imageUrl;
Phone({
required this.name,
required this.camera,
required this.ram,
required this.price,
required this.imageUrl,
});
factory Phone.fromMap(Map<String, dynamic> map) {
return Phone(
name: map['name'] as String? ?? '',
camera: map['camera'] as String? ?? '',
ram: map['ram'] as String? ?? '',
price: map['price'] as String? ?? '',
imageUrl: map['image'] as String? ?? '',
);
}
}
Complete page.
class TestPage extends StatelessWidget {
const TestPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: ListView.builder(
itemCount: mblist.length,
itemBuilder: (context, index) {
final phone = Phone.fromMap(mblist[index]);
return ListTile(
title: Text(phone.name),
subtitle: Row(
children: [
Text(phone.price),
const SizedBox(width: 10),
Text(phone.ram),
],
),
trailing:
Image.network(phone.imageUrl, width: 50, height: 50),
);
},
),
),
],
),
),
);
}
}
I believe dart:convert library straight forward and enough for your operation.It is coming as a standard library.

A RenderFlex overflowed by 99397 pixels on the bottom

import 'package:flutter/material.dart';
import 'package:http_request/carditem.dart';
import 'package:http_request/user_data.dart';
// here we chose to have statefull widget because the state needs to be updated and we need to update some variables according to some conditions.
class UserScreen extends StatefulWidget {
const UserScreen({Key key}) : super(key: key);
#override
_UserScreenState createState() => _UserScreenState();
}
class _UserScreenState extends State<UserScreen> {
// here we created a variable to keep our information in it . this information comes from our user data class where we get the user data from http requests.
Map<String, dynamic> userinfo = {};
// this method is called as soon as the main widget is created . we call our http method here to have the live data as soon as we open the app .be aware that we cant use async and await here . we just call the method.
#override
void initState() {
super.initState();
getData();
}
// here we get hte live data from our userdata class .
Future getData() async {
//we create a userData object to get access to the getuserdata method.
UserData userData = UserData();
//we handle any error here
try {
// here we create a variable to the exact same type we are expecting .
//then we wait for the result of calling our userdata information.
Map<String, dynamic> data = await userData.getUserData();
// we call setState because we need to update a certain value here.
setState(() {
//we assign the the value of data to our variable called userinfo .try the reaosn we see no error behind them is that they are the same type.
userinfo = data;
});
// we catch errors here .
} catch (e) {
print(e);
}
}
// our nice build method ...this method is from our stateful class.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Work Harder'),
centerTitle: true,
),
body: ListView.builder(
itemBuilder: (context, index) => CardItem(
firstName: userinfo['firstName'],
avatarLink: userinfo['avatarLink'],
id: userinfo['id'],
lastName: userinfo['lastName'],
email: userinfo['email'],
),
),
);
}
}
as you can see here is my user_screen dart file the problem is when i hot reload the app is see the renderflex error, while everything seems fine. im a begginner in flutter and i would really appreciate your help.
i have tried singlechild scrollview , list view , expanded widget , but it doesn't work . please help me hotrestart may app without these long errors
import 'package:flutter/material.dart';
// we chose stateless because we don't need to change the state of our app.
class CardItem extends StatelessWidget {
const CardItem({
Key key,
this.firstName,
this.email,
this.avatarLink,
this.id,
this.lastName,
}) : super(key: key);
final String firstName;
final String email;
final String avatarLink;
final String id;
final String lastName;
#override
Widget build(BuildContext context) {
return Card(
color: Colors.lightBlueAccent[200],
elevation: 3,
shadowColor: Colors.blue[100],
child: Column(
children: [
Stack(
children: [
ListTile(
leading: Icon(Icons.check_box),
trailing: CircleAvatar(
child: Image.network(avatarLink),
),
title: Text(firstName),
subtitle: Text(lastName),
),
Positioned(
left: 48,
top: 15,
child: Text(
id,
style: TextStyle(
color: Colors.white70,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
Positioned(
left: 120,
top: 30,
child: Text(
'Email : $email',
style: TextStyle(
color: Colors.blue[900].withOpacity(0.6),
),
),
),
],
),
],
),
);
}
}
here is my card widget , if it helps.
import 'dart:convert';
import 'package:http/http.dart' as http;
// this is the file for our main http requests.
class UserData {
UserData({
this.firstName,
this.lastName,
this.email,
this.id,
this.avatarLink,
});
final String firstName;
final String lastName;
final String email;
final String id;
final String avatarLink;
//this method is response for getting the live data we want also its responsible for decoding it with 'dart convert' library and returning the result as an output.
Future getUserData() async {
//the url we are working with
final String stringUrl = 'https://reqres.in/api/users?page=2';
//sending the request to web and storing the result to a variable. we also have to wait for the results . (asynchronous operation).
http.Response response = await http.get(
Uri.parse(stringUrl),
);
//we create a variable to the exact type of our output . we want to add our items to this map to use it in the main screen.
Map<String, dynamic> userdetails = {};
//here we want to check if our request was successful if so, we continue our operation and if not we throw an error .
if (response.statusCode == 200) {
// here we are decoding the data that we got from our request above to a variable of type map.
Map<String, dynamic> decodedData = jsonDecode(response.body);
//here we try to iterate through a map .. but im not sure if its correct or not .
for (var user in decodedData.values) {
Map<String, dynamic> myusersInfo = {
'firstName': decodedData['data'][0]['first_name'],
'lastName': decodedData['data'][0]['last_name'],
'email': decodedData['data'][0]['email'],
'id': decodedData['data'][0]['id'].toString(),
'avatarLink': decodedData['data'][0]['avatar'],
};
// here we are trying to add the items to the map specified.
userdetails.addEntries(myusersInfo.entries);
return userdetails;
}
} else {
print(response.statusCode);
throw 'Problem with the get request';
}
}
}
the whole goal of this app was to practice some http request.
the last file I have in this app . I just added this maybe it helps you . thanks in advance.
A Column likes to expand until infinity.
This can be solved by putting an intrinsicHeight around the Column as followed:
This class is useful, for example, when unlimited height is available and you would like a child that would otherwise attempt to expand infinitely to instead size itself to a more reasonable height.
IntrinsicHeight(
child: Column(
children: [
],
)
);
https://api.flutter.dev/flutter/widgets/IntrinsicHeight-class.html
try column inside SingleChildScrollView

How do I initialize my json response in another class that depends on a search input in flutter?

this is my first question here on stackoverflow.
I'm working on my first real app after attending a few CS classes at University and some courses on Udemy. So I'm lacking a lot of software engineering knowledge.
My goal: I want to build a search for stocks by using an external API endpoint. For that, I have created 4 dart files to handle the searchscreen with inputs (My UI), networking, parsing, and basically returning the data.
My NetworkAPI class to handle all sorts of network requests looks like this. I'm trying to use the jsonDecode already here and returning that. This class waits for an URL that will be put together in another class (financeData):
class NetworkAPI {
final String url;
NetworkAPI(this.url);
Future getData() async {
http.Response response = await http.get(url);
if (response.statusCode == 200) {
String data = response.body;
return jsonDecode(data);
} else {
print(response.statusCode);
}
}
}
This is basically the Json I want to parse. If I understood the theory correctly it's a map with a list of objects.
{
"bestMatches": [
{
"1. symbol": "TESO",
"2. name": "Tesco Corporation USA",
"3. type": "Equity",
"4. region": "United States",
"5. marketOpen": "09:30",
"6. marketClose": "16:00",
"7. timezone": "UTC-04",
"8. currency": "USD",
"9. matchScore": "0.8889"
}
{....}
]
}
To parse this, I saw some really good explanations here on stack overflow. I'm basically trying to retrieve the information I'm interested in.
class SearchOutput {
final List<BestMatch> bestMatches;
SearchOutput({this.bestMatches});
factory SearchOutput.fromJson(Map<String, dynamic> parsedJson){
var list = parsedJson['bestMatches'] as List;
print(list.runtimeType);
List<BestMatch> searchResultList = list.map((i) => BestMatch.fromJson(i)).toList();
return SearchOutput(
bestMatches: searchResultList,
);
}
}
class BestMatch {
String symbol;
String name;
String type;
String region;
String currency;
BestMatch({
this.symbol,
this.name,
this.type,
this.region,
this.currency,
});
factory BestMatch.fromJson(Map<String, dynamic> parsedJson){
return BestMatch(
symbol: parsedJson["1. symbol"],
name: parsedJson["2. name"],
type: parsedJson["3. type"],
region: parsedJson["4. region"],
currency: parsedJson["8. currency"],
);
}
}
Now I created a class with a method that should return all my data and format it into a list. The URL still needs an searchInput, which will be handed over from a TextField Widget in the UI.
const apiKey = 'demo';
const alphaVantageSearchUrl =
'https://www.alphavantage.co/query?function=SYMBOL_SEARCH';
class FinanceData {
Future<dynamic> getSearchData(String searchInput) async {
var url = '$alphaVantageSearchUrl&keywords=$searchInput&apikey=$apiKey';
NetworkAPI networkAPI = NetworkAPI(url);
var searchData = await networkAPI.getData();
SearchOutput searchOutput = new SearchOutput.fromJson(searchData);
return searchOutput;
}
}
Testing with some print statements seems to fullfill my goal of being able to access the data. For example printing out the name of the second object out of my response.
print(searchOutput.bestMatches[1].name);
The last file is my UI where we have a TextField, that I push to the financeData class in order to build the URL. Currently, my goal would be, to be able to show just the name of any given object in my response in the UI. I have no idea how to initialize my financeDate and how to use the return from my finaceData class in the UI. (I took some design styles out of the code snippet).
class SearchScreenWatchlist extends StatefulWidget {
#override
_SearchScreenWatchlistState createState() => _SearchScreenWatchlistState();
}
class _SearchScreenWatchlistState extends State<SearchScreenWatchlist> {
String searchInput;
FinanceData financeData = FinanceData();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Container(
child: TextField(
onChanged: (value) {
searchInput = value;
},
),
),
TextButton(
onPressed: () {
financeData.getSearchData(searchInput);
setState(() {
});
},
child: Text('Search')),
Text(('Search Results for: $searchInput')),
Container(child: Text('**SHOW HERE THE NAME**')),
],
),
);
}
}
It would be my goal to show in the last container the name on any given object from my response for example RESPONSE.name[1]. Later on I will try to iterate throw all the objects and show a list of all names of the response.
Thanks a lot! I really appreciate your help!
I think changing SearchScreenWatchlist to the following should display the name of the first bestMatches entry after you pressed the Search button (if I didn't miss something).
Make sure to look further into setState and initState (And the StatefulWidget lifecycle in general). Also it may not be the most elegant way to initialize FinanceData() and SearchOutput() this way - However, awesome starter project!
class SearchScreenWatchlist extends StatefulWidget {
#override
_SearchScreenWatchlistState createState() => _SearchScreenWatchlistState();
}
class _SearchScreenWatchlistState extends State<SearchScreenWatchlist> {
String searchInput;
FinanceData financeData;
SearchOutput searchOutput;
#override
initState() {
super.initState();
financeData = FinanceData();
searchOutput = SearchOutput();
}
_handleButtonPress() async {
final fetchedSearchOutput = await financeData.getSearchData(searchInput);
setState(() {
searchOutput = fetchedSearchOutput;
});
}
_buildSearchResult() {
if(searchOutput.bestMatches != null && searchOutput.bestMatches.isNotEmpty) {
return Text(searchOutput.bestMatches.first.name);
}
else {
return Text("No data fetched");
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Container(
child: TextField(
onChanged: (value) {
setState(() {
searchInput = value;
});
},
),
),
TextButton(
onPressed: () => _handleButtonPress(),
child: Text('Search'),
),
Text(('Search Results for: $searchInput')),
Container(
child: _buildSearchResult(),
),
],
),
);
}
}

How to retrieve data from phpmyadmin in dropdown form using flutter

I have to fetch data from the PHPMyAdmin and display it as a dropdown menu and I have a problem how to display the data in the dropdown menu. I want to create the registration form and get the dropdown menu list from DB.
This is my coding
class RegisterForm extends StatefulWidget{
#override
RegisterFormState createState(){
return RegisterFormState();
}
}
class RegisterFormState extends State<RegisterForm>{
String _mySelection;
List data=[];
final _formKey = GlobalKey<FormState>();
#override
void initState(){
fetchData();
super.initState();
}
void fetchData() async{
final response = await http.get('http://10.0.2.2/Shazay/getData.php');
if (response.statusCode == 200) {
setState(() {
data = json.decode(response.body);
});
}
}
and this is the coding for the dropdown menu:
new Row(
children: <Widget>[
new Text("Energy Bubble ",style: new TextStyle(fontSize: 18.0,color: Colors.blue),),
new DropdownButton(
items: data.map((item){
return new DropdownMenuItem(
child:new Text(item['EbName']),
value: item['EbName'].toString(),
);
}).toList(),
onChanged: (newVal){
setState(() {
_mySelection=newVal;
});
},
value: _mySelection,
}),
],
)
Edit original question is describe in Step 3
revised question is previously describe in Step 2 and need further info such as JSON string format from user
Step 1 : Get your JSON string get from mysql / PHPMyAdminservice
Step 2 : Transfer this JSON string to List of Map
Step 3 : convert it In items of DropdownButton which means generate DropdownMenuItem
code snippet from Flutter populate dropdownmenu with JSON
String _mySelection;
List<Map> _myJson = [{"id":0,"name":"<New>"},{"id":1,"name":"Test Practice"}];
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new DropdownButton<String>(
isDense: true,
hint: new Text("Select"),
value: _mySelection,
onChanged: (String newValue) {
setState(() {
_mySelection = newValue;
});
print (_mySelection);
},
items: _myJson.map((Map map) {
return new DropdownMenuItem<String>(
value: map["id"].toString(),
child: new Text(
map["name"],
),
);
}).toList(),
),
),
);
}

Decode Json from Youtube api on Flutter

I make a call to Youtube API and get this Json:
"kind": "youtube#videoListResponse",
"etag": "\"XI7nbFXulYBIpL0ayR_gDh3eu1k/s7-xmHXpuqQxYzDp_wxhm59K4LE\"",
"pageInfo": {
"totalResults": 1,
"resultsPerPage": 1
},
"items": [
{
"kind": "youtube#video",
"etag": "\"XI7nbFXulYBIpL0ayR_gDh3eu1k/pajQ7iBy-7A0V_owifxkw-Kbw-Y\"",
"id": "7lCDEYXw3mM",
"snippet": {
"publishedAt": "2012-06-20T23:12:38.000Z",
"channelId": "UC_x5XG1OV2P6uZZ5FSM9Ttw",
"title": "Google I/O 101: Q&A On Using Google APIs",
"description": "Antonio Fuentes speaks to us and takes questions on working with Google APIs and OAuth 2.0.",
"thumbnails": {
"default": {
"url": "https://i.ytimg.com/vi/7lCDEYXw3mM/default.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://i.ytimg.com/vi/7lCDEYXw3mM/mqdefault.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://i.ytimg.com/vi/7lCDEYXw3mM/hqdefault.jpg",
"width": 480,
"height": 360
}
But now I want to parse it and get just 3 nodes:
Title
Description
The url for the default thumbnail
Indeed I get the Json response and can see it on the logs, but when try to parse it every time it fails.
This is my code:
final response = await http.get(
'https://www.googleapis.com/youtube/v3/videos?id=HEREGOESMYAPIKEY&part=snippet&id=T0Jqdjbed40');
final parsed = json.decode(response.body).cast<Map<String, dynamic>>();
String title = parsed['items']['snippet']['title'];
print(title);
String description = parsed['items']['snippet']['description'];
print(description);
String thumbnail = parsed['items']['snippet']['thumbnails']['default']['url'];
print(thumbnail);
What you are trying won't work with Dart, this is not javascript. Dart has very strong type system, which is great. You are trying to assign value to a variable you've defined as String, but the response you have defined a dynamic, so Dart can't validate the value assignment. Also items is array, there is no such key items->snippet.
The correct way to do this is to create model deinitions, which will handle deserialisation and also will provide convenient way of accessing properties you are interested in.
class YoutubeResponse {
String kind;
String etag;
String nextPageToken;
String regionCode;
List<Item> items;
YoutubeResponse(
{this.kind,
this.etag,
this.nextPageToken,
this.regionCode,
this.items});
Map<String, dynamic> toJson() => {
'kind': kind,
'etag': etag,
'nextPageToken': nextPageToken,
'regionCode': regionCode,
'items': items,
};
factory YoutubeResponse.fromJSON(Map<String, dynamic> YoutubeResponseJson) {
var list = YoutubeResponseJson['items'] as List;
List<Item> itemsList = list.map((i) => Item.fromJSON(i)).toList();
return new YoutubeResponse(
kind: YoutubeResponseJson['kind'],
etag: YoutubeResponseJson['etag'],
nextPageToken: YoutubeResponseJson['nextPageToken'],
regionCode: YoutubeResponseJson['regionCode'],
mPageInfo: pageInfo.fromJSON(YoutubeResponseJson['pageInfo']),
items: itemsList);
}
}
class Item {
String kind;
String etag;
Id id;
Snippet snippet;
Item({this.kind, this.etag, this.id, this.snippet});
Map<String, dynamic> toJson() => {
'kind': kind,
'etag': etag,
'id': id,
'snippet': snippet,
};
factory Item.fromJSON(Map<String, dynamic> ItemJson) {
return Item(
kind: ItemJson['kind'],
etag: ItemJson['etag'],
id: Id.fromJSON(ItemJson['id']),
snippet: Snippet.fromJSON(ItemJson['snippet']),
);
}
}
class Snippet {
String publishedAt;
String channelId;
String title;
String description;
Thumbnails thumbnails;
String channelTitle;
String liveBroadcastContent;
Snippet(
{this.publishedAt,
this.channelId,
this.title,
this.description,
this.thumbnails,
this.channelTitle,
this.liveBroadcastContent});
Map<String, dynamic> toJson() => {
'publishedAt': publishedAt,
'channelId': channelId,
'title': title,
'description': description,
'thumbnails': thumbnails,
'channelTitle': channelTitle,
'liveBroadcastContent': liveBroadcastContent,
};
factory Snippet.fromJSON(Map<String, dynamic> SnippetJson) {
return Snippet(
publishedAt: SnippetJson['publishedAt'],
channelId: SnippetJson['channelId'],
title: SnippetJson['title'],
description: SnippetJson['description'],
thumbnails: Thumbnails.fromJSON(SnippetJson['thumbnails']) ,
channelTitle: SnippetJson['channelTitle'],
liveBroadcastContent: SnippetJson['liveBroadcastContent'],
);
}
}
class Medium {
int height;
int width;
String url;
Medium({this.height, this.width, this.url});
Map<String, dynamic> toJson() => {
'height': height,
'width': width,
'url': url,
};
factory Medium.fromJSON(Map<String, dynamic> MediumJson) {
return Medium(
height: MediumJson['height'],
width: MediumJson['width'],
url: MediumJson['url'],
);
}
}
class High {
int height;
int width;
String url;
High({this.height, this.width, this.url});
Map<String, dynamic> toJson() => {
'height': height,
'width': width,
'url': url,
};
factory High.fromJSON(Map<String, dynamic> HighJson) {
return High(
height: HighJson['height'],
width: HighJson['width'],
url: HighJson['url'],
);
}
}
class Default {
int height;
int width;
String url;
Default({this.height, this.width, this.url});
Map<String, dynamic> toJson() => {
'height': height,
'width': width,
'url': url,
};
factory Default.fromJSON(Map<String, dynamic> defaultJson) {
return Default(
height: defaultJson['height'],
width: defaultJson['width'],
url: defaultJson['url'],
);
}
}
class Thumbnails {
Default mDefault;
Medium medium;
High high;
Thumbnails({this.mDefault, this.medium, this.high});
var data = JsonEncoder().convert("");
Map<String, dynamic> toJson() => {
'default': mDefault,
'medium': medium,
'high': high,
};
factory Thumbnails.fromJSON(Map<String, dynamic> ThumbnailsJson) {
return Thumbnails(
mDefault: Default.fromJSON(ThumbnailsJson['default']),
medium: Medium.fromJSON(ThumbnailsJson['medium']),
high: High.fromJSON(ThumbnailsJson['high']),
);
}
}
Now that we have described the format of the JSON we expect it is very easy to parse it:
YoutubeResponse parsedResponse =
YoutubeResponse.fromJSON(json.decode(response.body));
You can then access the items via parsedResponse.items, then loop through them and get the title, descriptions etc.
To further the answer by #Shaddy (which works) here is my class that calls the network
class VideoNetwork {
static Future<List<Item>> fetchPost() async {
final response =
await http.get(<URL for Youtube Videos>);
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
return compute(parseVideos, response.body);
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
static List<Item> parseVideos(String responseBody) {
YoutubeResponse response =
YoutubeResponse.fromJSON(json.decode(responseBody));
return response.items.toList();
}
}
Might be bit late to answer the question.
If you want to learn how to parse complex json data use these articles.
1. Parsing JSON in the background - Flutter cookbook
and
Parsing complex JSON in Flutter- medium.com by Pooja Bhaumik
FYI - Youtube data api response is in a nested Json format
I used a alternative way, this is not recommended for big applications(I was just playing around with it and this is only for above type json tree)
Use async function to retrieve and parse Json data.
final String dataUrl = "YOUR-JSON-URL";
Future<String> getdet() async {
var response = await http.get(Uri.encodeFull(dataUrl), headers: {"Accept": "application/json"});
if (response.statusCode == 200) {
var responseBody = json.decode(response.body);
convertedData = responseBody["items"];
} else {
throw Exception('Failed to Load Data');
}
"items " is the array starting point
after that you can use it in a Widget for example
Widget recentWidget(){
return ListView.builder(
itemCount: convertedData == null ? 0 : recent.length,
itemBuilder: (BuildContext context, int index, ) {
return Column(
children: <Widget>[
Card(
child: Column(
children: <Widget>[
new Image.network(recent[index]["snippet"]["thumbnails"]["medium"]["url"]),
new ListTile(
title: new Text(recent[index]["snippet"]["title"]),
subtitle: Text("This is Subtitle"),
},
dense: true,
),
],
),
)
],
);
},shrinkWrap: true,
physics: ClampingScrollPhysics(),
)
}
Hope this helps.
The answer of #Sh1d0w , was good but it has a lack of content for people who doesnt know so much of flutter itself. I have to say thanks Sh1d0w since you give me the base to create my own code to fetch the data using the youtube url.
I combined his answer using a SearchDelegate in order to show the results , I still need to add the code to show the next/previous page results (youtube only give 50 results by page) but to answer your questions here is the code.
At the end you will be able to get the follow:
publishedAt
channelId
title
description
thumbnails (default, high, medium)
liveBroadcastContent
videoId
If you want any other data from the json, you will need to add what you want in the Class Snippet to be returned and used later.
Since I am using a futureBuilder which give the data in the snapshot.data object, you will need to use this kind of declaration to get each property:
snapshot.data[index].title
snapshot.data[index].description
snapshot.data[index].thumbnails.high.url
**In the code you will see things like appTheme.text , etc those are just variables of colors, please change them for your colors.
** the functions like sizeBuild() or fontSizeBuild() are functions which I created for my case, you just need to delete those lines and write any number according to your needs
common_youtubeApi.dart
import 'dart:convert';
class YoutubeResponse{
//!-1st level parameters of youtube api for playlist
//!-https://developers.google.com/youtube/v3/docs/playlistItems/list#response
String kind;
String etag;
String nextPageToken;
String prevPageToken;
String regionCode;
List<Item> items;
//change the default values for the obtained values from url
YoutubeResponse({
this.kind,
this.etag,
this.nextPageToken,
this.prevPageToken,
this.regionCode,
this.items
});
//Json decode and make a dart object called Map
Map<String, dynamic> toJson() => {
'kind': kind,
'etag': etag,
'nextPageToken': nextPageToken,
'prevPageToken': prevPageToken,
'regionCode': regionCode,
'items': items,
};
factory YoutubeResponse.fromJSON(Map<String, dynamic> YoutubeResponseJson){
var list = YoutubeResponseJson['items'] as List;
List<Item> itemsList = list.map((i)=> Item.fromJSON(i)).toList();
return new YoutubeResponse(
kind: YoutubeResponseJson['kind'],
etag: YoutubeResponseJson['etag'],
nextPageToken: YoutubeResponseJson['nextPageToken'],
prevPageToken: YoutubeResponseJson['prevPageToken'],
regionCode: YoutubeResponseJson['regionCode'],
// mPageInfo: pageInfo.fromJSON(YoutubeResponseJson['pageInfo']),
items: itemsList
);
}
}
//---------Create an single video item
class Item{
String kind;
String etag;
String id;
Snippet snippet;
Item({
this.kind, this.etag, this.id, this.snippet
});
Map<String, dynamic> toJson() => {
'kind': kind,
'etag': etag,
'id': id,
'snippet': snippet,
};
factory Item.fromJSON(Map<String, dynamic> ItemJson) {
return Item(
kind: ItemJson['kind'],
etag: ItemJson['etag'],
id: ItemJson['id'],
snippet: Snippet.fromJSON(ItemJson['snippet']),
);
}
}
class Snippet {
String publishedAt;
String channelId;
String title;
String description;
Thumbnails thumbnails;
String channelTitle;
String liveBroadcastContent;
String videoId;
Snippet(
{this.publishedAt,
this.channelId,
this.title,
this.description,
this.thumbnails,
this.channelTitle,
this.liveBroadcastContent,
this.videoId,
});
Map<String, dynamic> toJson() => {
'publishedAt': publishedAt,
'channelId': channelId,
'title': title,
'description': description,
'thumbnails': thumbnails,
'channelTitle': channelTitle,
'liveBroadcastContent': liveBroadcastContent,
'videoId': videoId,
};
factory Snippet.fromJSON(Map<String, dynamic> snippetJson) {
return Snippet(
publishedAt: snippetJson['publishedAt'],
channelId: snippetJson['channelId'],
title: snippetJson['title'],
description: snippetJson['description'],
thumbnails: Thumbnails.fromJSON(snippetJson['thumbnails']) ,
channelTitle: snippetJson['channelTitle'],
liveBroadcastContent: snippetJson['liveBroadcastContent'],
videoId: snippetJson['resourceId']['videoId'],
);
}
}
class Medium {
int height;
int width;
String url;
Medium({this.height, this.width, this.url});
Map<String, dynamic> toJson() => {
'height': height,
'width': width,
'url': url,
};
factory Medium.fromJSON(Map<String, dynamic> MediumJson) {
return Medium(
height: MediumJson['height'],
width: MediumJson['width'],
url: MediumJson['url'],
);
}
}
class High {
int height;
int width;
String url;
High({this.height, this.width, this.url});
Map<String, dynamic> toJson() => {
'height': height,
'width': width,
'url': url,
};
factory High.fromJSON(Map<String, dynamic> HighJson) {
return High(
height: HighJson['height'],
width: HighJson['width'],
url: HighJson['url'],
);
}
}
class Default {
int height;
int width;
String url;
Default({this.height, this.width, this.url});
Map<String, dynamic> toJson() => {
'height': height,
'width': width,
'url': url,
};
factory Default.fromJSON(Map<String, dynamic> defaultJson) {
return Default(
height: defaultJson['height'],
width: defaultJson['width'],
url: defaultJson['url'],
);
}
}
class Thumbnails {
Default mDefault;
Medium medium;
High high;
Thumbnails({this.mDefault, this.medium, this.high});
var data = JsonEncoder().convert("");
Map<String, dynamic> toJson() => {
'default': mDefault,
'medium': medium,
'high': high,
};
factory Thumbnails.fromJSON(Map<String, dynamic> thumbnailsJson) {
return Thumbnails(
mDefault: Default.fromJSON(thumbnailsJson['default']),
medium: Medium.fromJSON(thumbnailsJson['medium']),
high: High.fromJSON(thumbnailsJson['high']),
);
}
}
searchList.dart
files to import:
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:denApp/keys/app_keys.dart'; //put your keys always in a different file with gitignore
import 'package:url_launcher/url_launcher.dart'; //for links in each item
import 'package:denApp/Widgets/common_youtubeAPI.dart'; //here are the models of each data requested in the json to be used here
class DataSearch extends SearchDelegate<List>{
var nextPageToken;
var prevPageToken;
Future<void> _launched;
//ajax/http request for data
Future<List<Snippet>> getVideos(http.Client client) async {
YoutubeResponse parsedResponse;
final response = await client
.get('https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=$youtubeTrickPlaylist&key=$youtubeAPIKEY&maxResults=50');
List<Snippet> videos = [];
parsedResponse = YoutubeResponse.fromJSON(json.decode(response.body));
for(var i=0; i < parsedResponse.items.length; i++ ){
videos.add(parsedResponse.items[i].snippet);
}
this.nextPageToken = parsedResponse.nextPageToken;
this.prevPageToken = parsedResponse.prevPageToken;
print(this.nextPageToken);
print(this.prevPageToken);
return videos;
}
//We use the launcher plugin to manage the click to go to the website, please visit the plugin web for info how to use it
Future<void> _launchInBrowser(String url) async {
print(url);
if (await canLaunch(url)) {
await launch(
url,
forceSafariVC: false,
forceWebView: false,
headers: <String, String>{'my_header_key': 'my_header_value'},
);
} else {
throw 'Could not launch $url';
}
}
//------------------------------------------------------
//--This part is to edit the colors and design of the searchDelegate widget, I have a separated file with the themes, colors which I call using appTheme, so you need to change all those variables with your own colors.
#override
ThemeData appBarTheme(BuildContext context) {
return ThemeData(
primaryColor: appTheme.text,
backgroundColor: appTheme.dark,
bottomAppBarColor: appTheme.dark,
canvasColor: appTheme.dark,
);
}
//-------------------------------------------------------------
//---Here we define how it will works the SearchDelegate and its icons/functions-----------
#override
List<Widget> buildActions(BuildContext context) {
// ----This is the icon which will delete whatever you write in your searchbar
return [IconButton(icon: Icon(Icons.clear), onPressed: () {
query="";
},)];
}
#override
Widget buildLeading(BuildContext context) {
// ---- This is the icon which will allow you to close the search delegate going to the back page.
return IconButton(icon: Icon(Icons.arrow_back), onPressed: (){
close(context, null);
});
}
#override
Widget buildResults(BuildContext context) {
// nothing here
throw UnimplementedError();
}
#override
Widget buildSuggestions(BuildContext context) {
// This is actually the place where you will do all the work when you receive the data from your future request. Since we are going to build something after we wait for the data, we need to use futureBuilder instead of builder only. In the future propiety you will put the function which will do the request to the api
return FutureBuilder<List<Snippet>>(
future: getVideos(http.Client()),
builder: (context, snapshot) {
//during the build you need to check the connection state so depending on what is happening you will show something , like a loader if it is waiting state, etc
switch (snapshot.connectionState){
case ConnectionState.none:
return Text('none', style: TextStyle(color:Colors.black));
case ConnectionState.active:
return Text('active', style: TextStyle(color:Colors.black));
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
default:
//after checking connection we need to check if we do not got null data from the request
if(snapshot.data != null){
var dt = snapshot.data.where((p)=> p.title.toLowerCase().contains(query.toLowerCase())).toList();
return dt.isEmpty? Center(child:Text("There was no results for your query", style: TextStyle(color: appTheme.text))) : ListView.builder(
itemCount: dt.length,
padding: EdgeInsets.zero,
itemBuilder: (context, index) {
if(query.isEmpty){
return Card(
margin: (index == (dt.length - 1)) ? EdgeInsets.only(top: sizeBuild(context, "height", 5), bottom: sizeBuild(context, "height", 15)) : EdgeInsets.only(top: sizeBuild(context, "height", 5)),
color: appTheme.bgLight.withOpacity(.1),
child: InkWell(
splashColor: appTheme.linkB.withAlpha(30),
onTap: () {
_launched = _launchInBrowser('https://www.youtube.com/watch?v=${snapshot.data[index].videoId}');
},
child: Container(
child: Row(
children: <Widget>[
Container(
width:sizeBuild(context, "width", 140),
child: Image.network("${snapshot.data[index].thumbnails.high.url}", fit: BoxFit.cover),
),
Container(
// color: Colors.red,
margin: EdgeInsets.symmetric(horizontal: sizeBuild(context, "width",10)),
width:sizeBuild(context, "width", 280),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(snapshot.data[index].title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle (
color: appTheme.text,
fontWeight: FontWeight.bold,
fontSize: fontSizeBuild(context, 'body')
),
),
SizedBox(
height: sizeBuild(context, "height", 5),
),
Text(snapshot.data[index].description,
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: TextStyle (
color: appTheme.text.withOpacity(.7),
fontSize: fontSizeBuild(context, 'small')
),
),
],
),
),
],
),
),
),
);
}else{
return Card(
margin: (index == (dt.length - 1)) ? EdgeInsets.only(top: sizeBuild(context, "height", 5), bottom: sizeBuild(context, "height", 15)) : EdgeInsets.only(top: sizeBuild(context, "height", 5)),
color: appTheme.bgLight.withOpacity(.1),
child: InkWell(
splashColor: appTheme.linkB.withAlpha(30),
onTap: () {
_launched = _launchInBrowser('https://www.youtube.com/watch?v=${snapshot.data[index].videoId}');
},
child: Container(
child: Row(
children: <Widget>[
Container(
width:sizeBuild(context, "width", 140),
child: Image.network("${snapshot.data[index].thumbnails.high.url}", fit: BoxFit.cover),
),
Container(
margin: EdgeInsets.symmetric(horizontal: sizeBuild(context, "width",10)),
width:sizeBuild(context, "width", 280),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(snapshot.data[index].title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle (
color: appTheme.text,
fontWeight: FontWeight.bold,
fontSize: fontSizeBuild(context, 'body')
),
),
SizedBox(
height: sizeBuild(context, "height", 20),
),
Text(snapshot.data[index].description,
maxLines: 5,
overflow: TextOverflow.ellipsis,
style: TextStyle (
color: appTheme.text.withOpacity(.7),
fontSize: fontSizeBuild(context, 'small')
),
),
],
),
),
],
),
),
),
);
}
},
);
}else{
//this is the widget to give if there is no data
return Center(child: Text("There was no data from the server", style: TextStyle(color:appTheme.text)));
}
}
},
);
}
}
The problem in your code snippet is much more simple than the other answers would imply.
First of all, your JSON string might be wrong, you left out a couple of things. You might have also forgotten to unescape the etag values: "etag": "\\\"XI7nbFXulYBIpL0ayR_gDh3eu1k/s7-xmHXpuqQxYzDp_wxhm59K4LE\\\"",. Of course, all of these could be just copying errors.
You can create extra classes, and if you want to maintain your code for the long term, you absolutely should. There are code generators that will do most of the work for you. However, you don't need to create those humongous classes, just to get out 3 values of that JSON. You also don't need to spin up on a different isolate, that JSON is tiny (for the computer), and you won't notice any performance issues from parsing that.
There is nothing wrong with the approach you took. It's a simple solution that works, it's great for very simple scripts and apps.
The only thing is that you forgot that the items is a list, so you need to first take the 0th element in that list. After that fix, everything works as expected: parsed['items'][0]['snippet']['title'].
You can copy this to dartpad.dev and try it out yourself.
import 'dart:convert';
void main() {
final parsed = jsonDecode(responseBody);
String title = parsed['items'][0]['snippet']['title'];
String description = parsed['items'][0]['snippet']['description'];
String thumbnail = parsed['items'][0]['snippet']['thumbnails']['default']['url'];
print('title: $title');
print('description: $description');
print('thumbnail: $thumbnail');
}
const responseBody = '''{
"kind": "youtube#videoListResponse",
"etag": "\\\"XI7nbFXulYBIpL0ayR_gDh3eu1k/s7-xmHXpuqQxYzDp_wxhm59K4LE\\\"",
"pageInfo": {
"totalResults": 1,
"resultsPerPage": 1
},
"items": [
{
"kind": "youtube#video",
"etag": "\\\"XI7nbFXulYBIpL0ayR_gDh3eu1k/pajQ7iBy-7A0V_owifxkw-Kbw-Y\\\"",
"id": "7lCDEYXw3mM",
"snippet": {
"publishedAt": "2012-06-20T23:12:38.000Z",
"channelId": "UC_x5XG1OV2P6uZZ5FSM9Ttw",
"title": "Google I/O 101: Q&A On Using Google APIs",
"description": "Antonio Fuentes speaks to us and takes questions on working with Google APIs and OAuth 2.0.",
"thumbnails": {
"default": {
"url": "https://i.ytimg.com/vi/7lCDEYXw3mM/default.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://i.ytimg.com/vi/7lCDEYXw3mM/mqdefault.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://i.ytimg.com/vi/7lCDEYXw3mM/hqdefault.jpg",
"width": 480,
"height": 360
}
}
}
}
]
}''';