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
Related
I have a use case where I need to change the app bar height with a key value of JSON.
I have this Scaffold which shows a plain appbar and the height of the appbar which is 50px:
# app_base.dart
class AppBaseStack extends StatefulWidget {
const AppBaseStack({Key? key}) : super(key: key);
#override
_AppBaseStackState createState() => _AppBaseStackState();
}
class _AppBaseStackState extends State<AppBaseStack> {
double? heightAppBar;
#override
initState() {
loadAppbarHeight();
debugPrint(heightAppBar.toString());
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(heightAppBar),
child: AppBar(),
),
);
}
loadAppbarHeight() async {
final String response = await rootBundle.loadString('[route]/file.json');
final data = json.decode(response);
setState(() {
heightAppBar = double.parse(data["app_bar"]["app_bar_height"]);
});
}
}
This is the JSON file which I want to grab the key value of app_bar_height to change the height of the appbar:
#app_style.json
{
"app_bar":
{
"app_bar_height": "50"
}
}
I want to grab the JSON and then grab key app_bar_height and read the value of this key. Then I want to change the height of the app bar with this key value. And if I want to change the app bar height I want to do so by changing the key value of app_bar_height from 50 to for example 60 in the JSON file itself.
I don't know how I can do this I tried many things but I want to know if there is an acceptable way in flutter/dart.
EDIT
So i made some changes in the app_base.dart file. I added the method which loads the json and grabs the json and grabs the key app_bar_height. I also added a initState which initializes the method but i get an error: Null check operator used on a null value
for some reason heightAppBar is null how can i fix this?
In initState (before the view is loaded) call a method that does this:
final String response = await rootBundle.loadString('[route]/file.json');
final data = json.decode(response);
setState(() {
heightAppBar = double.parse(data["app_bar"]["app_bar_height"]);
});
The method is async, so you need to set a default value to heightAppBar and then update.
this is what I use in my project. you can pass height parameter while adding your project.
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
final List<Widget>? actions;
final String? text;
final TabBar? bottom;
final Widget? title;
final bool? disableLeading;
final Function? leadingAction;
CustomAppBar({
Key? key,
this.actions,
this.disableLeading,
this.text,
this.title,
this.bottom,
this.leadingAction,
}) : preferredSize = Size.fromHeight(bottom != null ? 120 : 60),
super(key: key);
#override
Widget build(BuildContext context) {
return AppBar(
backgroundColor: primaryColor,
elevation: 0,
iconTheme: IconThemeData(color: Colors.white),
centerTitle: Platform.isIOS ? true : false,
title: this.title??AutoSizeText(text??"",
minFontSize: 11,
maxFontSize: 18,
maxLines: 1,
overflow: TextOverflow.fade,
style: TextStyle(color: Colors.white, fontWeight: FontWeight.w700)),
leading: disableLeading!=null?Container():SizedBox(
child: IconButton(
icon: Icon(
FontAwesomeIcons.chevronLeft,
color: Colors.white,
),
onPressed: () {
Get.back();
if(leadingAction!= null)
leadingAction!();
})),
actions: this.actions,
bottom: this.bottom,
);
}
#override
Size preferredSize;
}
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!
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(),
),
],
),
);
}
}
i'm confusing with this kind of json.
because i never see someone explain about this kind of json data.
i get my jsondata from a link, and the json data show like this
{
"data": [
{
"jenis_komoditas": "Gabah IR 64 KP",
"harga": "5400",
"satuan": "Kg",
"persen": null,
"perubahan": "0",
"selisi": "0",
"image": "assets\/thumb\/gabah.png"
},
{
"jenis_komoditas": "Gabah IR 64 KG",
"harga": "6200",
"satuan": "Kg",
"persen": null,
"perubahan": "0",
"selisi": "0",
"image": "assets\/thumb\/gabah1.png"
}
]
}
it gets error.
can someone help me how to get the data from first json type?
You can parse your full json string with
Payload payload = payloadFromJson(jsonString);
print('${payload.data[0].jenisKomoditas}');
Related Class
// To parse this JSON data, do
//
// final payload = payloadFromJson(jsonString);
import 'dart:convert';
Payload payloadFromJson(String str) => Payload.fromJson(json.decode(str));
String payloadToJson(Payload data) => json.encode(data.toJson());
class Payload {
List<Datum> data;
Payload({
this.data,
});
factory Payload.fromJson(Map<String, dynamic> json) => Payload(
data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"data": List<dynamic>.from(data.map((x) => x.toJson())),
};
}
class Datum {
String jenisKomoditas;
String harga;
String satuan;
dynamic persen;
String perubahan;
String selisi;
String image;
Datum({
this.jenisKomoditas,
this.harga,
this.satuan,
this.persen,
this.perubahan,
this.selisi,
this.image,
});
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
jenisKomoditas: json["jenis_komoditas"],
harga: json["harga"],
satuan: json["satuan"],
persen: json["persen"],
perubahan: json["perubahan"],
selisi: json["selisi"],
image: json["image"],
);
Map<String, dynamic> toJson() => {
"jenis_komoditas": jenisKomoditas,
"harga": harga,
"satuan": satuan,
"persen": persen,
"perubahan": perubahan,
"selisi": selisi,
"image": image,
};
}
full code
import 'package:flutter/material.dart';
// To parse this JSON data, do
//
// final payload = payloadFromJson(jsonString);
import 'dart:convert';
Payload payloadFromJson(String str) => Payload.fromJson(json.decode(str));
String payloadToJson(Payload data) => json.encode(data.toJson());
class Payload {
List<Datum> data;
Payload({
this.data,
});
factory Payload.fromJson(Map<String, dynamic> json) => Payload(
data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"data": List<dynamic>.from(data.map((x) => x.toJson())),
};
}
class Datum {
String jenisKomoditas;
String harga;
String satuan;
dynamic persen;
String perubahan;
String selisi;
String image;
Datum({
this.jenisKomoditas,
this.harga,
this.satuan,
this.persen,
this.perubahan,
this.selisi,
this.image,
});
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
jenisKomoditas: json["jenis_komoditas"],
harga: json["harga"],
satuan: json["satuan"],
persen: json["persen"],
perubahan: json["perubahan"],
selisi: json["selisi"],
image: json["image"],
);
Map<String, dynamic> toJson() => {
"jenis_komoditas": jenisKomoditas,
"harga": harga,
"satuan": satuan,
"persen": persen,
"perubahan": perubahan,
"selisi": selisi,
"image": image,
};
}
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: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
String jsonString = '''
{
"data": [
{
"jenis_komoditas": "Gabah IR 64 KP",
"harga": "5400",
"satuan": "Kg",
"persen": null,
"perubahan": "0",
"selisi": "0",
"image": "assets\/thumb\/gabah.png"
},
{
"jenis_komoditas": "Gabah IR 64 KG",
"harga": "6200",
"satuan": "Kg",
"persen": null,
"perubahan": "0",
"selisi": "0",
"image": "assets\/thumb\/gabah1.png"
}
]
}
''';
void _incrementCounter() {
Payload payload = payloadFromJson(jsonString);
print('${payload.data[0].jenisKomoditas}');
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Output
I/flutter (23421): Gabah IR 64 KP
you are getting data in form of json array so you can simply call it iteratively from the object. like this
if you get data in data variable:
final jsondata=json.decode(data);
for (var dataf in jsondata['data']){
print(dataf['jenis_komoditas']);
}
I want to serialize JSON objects to my native class objects from an incoming API request. However, when the mapping happens and the FutureBuilder tries to return data, it generates a runtime error as follows:
type _CastError is not a subtype of bool
while using a list to return data and
type _TypeError is not a subtype of bool
From the errors I am assuming that it is not able to cast the data from the stream. I don’t have many ideas as I am learning flutter.
Here is the future through which I am making a request:
Future getWeatherInfo(String city) async {
Uri uri = new Uri.https("api.apixu.com", "/v1/current.json",
{"key": <key>, "q": city});
print(uri);
final response = await http.get(uri);
final responseJSON = jsonDecode(response.body);
print(responseJSON);
return BaseClass.getData(responseJSON);
Native classes:
class BaseClass {
// final WeatherInformation weatherInformation;
final Weather weather;
BaseClass({this.weather});
factory BaseClass.getData(Map jsonData) {
return new BaseClass(
// weatherInformation: (jsonData["location"])
// .map((i) => WeatherInformation.weatherValues(i)),
weather: (jsonData["current"])
.map((i) => Weather.weatherData(i)),
);
}}
class WeatherInformation {
final String cityName;
final String cityRegion;
final String country;
final double lat;
final double long;
final String time;
WeatherInformation({
this.cityName,
this.cityRegion,
this.country,
this.time,
this.lat,
this.long,
});
factory WeatherInformation.weatherValues(Map data) {
return new WeatherInformation(
cityRegion: data['cityRegion'],
cityName: data['name'],
country: data['country'],
lat: data['lat'],
long: data['long'],
time: data['localtime'],
);
}
}
class Weather {
final Condition condition;
final double windmph;
final double windkph;
final double winddegree;
final int cloud;
final double pressuremb;
final double pressurein;
final double precipmm;
final double precipin;
final int humidity;
final double centrigade;
final double faranheit;
final double tempc;
final double tempf;
Weather({
this.centrigade,
this.cloud,
this.faranheit,
this.humidity,
this.pressuremb,
this.tempc,
this.precipmm,
this.precipin,
this.pressurein,
this.tempf,
this.winddegree,
this.windkph,
this.windmph,
this.condition,
});
factory Weather.weatherData(Map data) {
return new Weather(
tempc: data['temp_c'],
tempf: data['temp_f'],
windmph: data['wind_mph'],
windkph: data['wind_kph'],
winddegree: data['wind_degree'],
pressuremb: data['pressure_mb'],
pressurein: data['pressure_in'],
precipmm: data['precip_mm'],
precipin: data['precip_in'],
humidity: data['humidity'],
cloud: data['cloud'],
centrigade: data['feelslike_c'],
faranheit: data['feelslike_f'],
// condition:
// (data['condition'] as List).map((i) => Condition.toJSON(i)).toList(),
);
}
}
class Condition {
String text;
String icon;
Condition({
Key key,
this.text,
this.icon,
});
Condition.toJSON(Map data)
: icon = data['icon'],
text = data['text'];
}
ListView:
** if (snapshot.hasError)** { //(previously was snapshot.error)
return new Center(
child: new Chip(
label: new Text('Error! Please try again',style: new TextStyle(color:
Colors.white),)));
} else if (snapshot.hasData) {
return new ListView(
shrinkWrap:false,
children: <Widget>[
new ListTile(
leading: new Text("Calvin"),
title: new
Text(snapshot.data.weather[0].tempc.toString(),
style: new TextStyle(color: Colors.white)),),
new ListTile(
leading: new Text('Pressure'),
title: new Text(
snapshot.data.weather[0].pressurein.toString(),
style: new TextStyle(color: Colors.white)),
),
new ListTile(
title: new Text(snapshot.data.weather[0].cloud.toString()),
leading: new Icon(Icons.cloud)),
],
);
}
There is no type in the Future. Due to that, the AsyncSnapshot may not be working correctly.
Use Future<BaseClass> in the first listing above,
Then, in the FutureBuilder add it as follows:
new FutureBuilder<BaseClass>(
future: getWeatherInfo(...),
builder: (BuildContext context, AsyncSnapshot<BaseClass> snapshot) {
....