Last active
May 12, 2022 12:55
-
-
Save dhruvilp/ba09cefe0e9b1c0a8624c3e339f406ab to your computer and use it in GitHub Desktop.
Flutter Workshop Demo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Get more Flutter packages and plugins at https://pub.dev | |
import 'package:flutter/material.dart'; // Material UI widgets | |
import 'package:http/http.dart' as http; // http package | |
import 'dart:convert'; // json handler package | |
import 'defaults.dart'; // default strings and constants | |
// ============================================================================= | |
// FILE: weather_model.dart | |
// ============================================================================= | |
// Weather class to consume JSON data | |
class WeatherModel { | |
String? cityName; | |
double? temp; | |
double? wind; | |
int? humidity; | |
double? feelsLike; | |
int? pressure; | |
String? icon; | |
int? weatherId; | |
String? weatherType; | |
String? weatherDescription; | |
int? sunrise; | |
int? sunset; | |
WeatherModel( | |
this.cityName, | |
this.temp, | |
this.wind, | |
this.humidity, | |
this.feelsLike, | |
this.pressure, | |
this.icon, | |
this.weatherId, | |
this.weatherType, | |
this.weatherDescription, | |
this.sunrise, | |
this.sunset, | |
); | |
WeatherModel.fromJson(Map<String, dynamic> json) { | |
cityName = json['name']; | |
temp = json['main']['temp']; | |
wind = json['wind']['speed']; | |
humidity = json['main']['humidity']; | |
feelsLike = json['main']['feels_like']; | |
pressure = json['main']['pressure']; | |
icon = json['weather'][0]['icon']; | |
weatherId = json['weather'][0]['id']; | |
weatherType = json['weather'][0]['main']; | |
weatherDescription = json['weather'][0]['description']; | |
sunrise = json['sys']['sunrise']; | |
sunset = json['sys']['sunset']; | |
} | |
} | |
// ============================================================================= | |
// FILE: api_service.dart | |
// ============================================================================= | |
// Weather API Service class | |
class ApiService { | |
// Http [GET] request to fetch weather data for a given CITY_NAME | |
static Future<WeatherModel> getWeatherDataByLocation(String location) async { | |
// If you want to use this API, then get an APP_ID from here: https://api.openweathermap.org | |
var endpoint = Uri.parse( | |
'https://api.openweathermap.org/data/2.5/weather?q=$location&appid=$kApiKey&units=metric'); | |
var response = await http | |
.get(endpoint); // Http package support GET, POST, PUT, DELETE, etc. | |
var body = jsonDecode(response.body); | |
// debugPrint("=== body === $body"); // use "debugPrint" to print values in DEV [recommended] | |
// // === FOR DEVELOPMENT: Use static data to avoid getting charged for API calls | |
// var body = jsonDecode('{"coord":{"lon":-71.0598,"lat":42.3584},"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],"base":"stations","main":{"temp":15.1,"feels_like":13.79,"temp_min":13.16,"temp_max":16.43,"pressure":1017,"humidity":43},"visibility":10000,"wind":{"speed":2.06,"deg":190},"clouds":{"all":100},"dt":1651844110,"sys":{"type":2,"id":2013408,"country":"US","sunrise":1651829576,"sunset":1651880923},"timezone":-14400,"id":4930956,"name":"Boston","cod":200}'); | |
return WeatherModel.fromJson(body); | |
} | |
} | |
// ============================================================================= | |
// FILE: weather_app.dart | |
// ============================================================================= | |
// ============================================================================= | |
// Stateful WeatherApp Widget (Main UI) | |
// | |
// 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. | |
// ============================================================================= | |
class WeatherApp extends StatefulWidget { | |
final String appTitle; | |
const WeatherApp({ | |
Key? key, | |
required this.appTitle, | |
}) : super(key: key); | |
@override | |
State<WeatherApp> createState() => _WeatherAppState(); | |
} | |
class _WeatherAppState extends State<WeatherApp> { | |
WeatherModel? data; | |
Color? bgColor = Colors.cyan; | |
@override | |
void initState() { | |
getWeatherData('mumbai'); | |
super.initState(); | |
} | |
// [APPROACH 1] One way of calling an api service | |
void getWeatherData(String cityData) { | |
ApiService.getWeatherDataByLocation(cityData).then((data) { | |
setState(() { | |
data = data; | |
}); | |
}); | |
} | |
// [APPROACH 2] Another way of calling an api service | |
Future<WeatherModel> getData() async { | |
// new york city, los angeles, boston, chicago | |
data = await ApiService.getWeatherDataByLocation('mumbai'); | |
return data!; | |
} | |
// A Widget to show weather details like Wind, Humidity, Pressure, etc | |
Widget _detailCard( | |
String name, String value, String unit, BuildContext context) { | |
var screenWidth = MediaQuery.of(context).size.width; | |
var textColor = Colors.green[100]; | |
return Expanded( | |
child: Card( | |
color: Colors.green[900], | |
margin: const EdgeInsets.symmetric(horizontal: 5.0), | |
shape: const RoundedRectangleBorder( | |
borderRadius: BorderRadius.all(Radius.circular(50.0)), | |
), | |
child: Padding( | |
padding: const EdgeInsets.all(4.0), | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.spaceEvenly, | |
children: [ | |
Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
// Alternatively can use [Text] widget | |
SelectableText( | |
value, | |
style: TextStyle( | |
fontSize: screenWidth < 760 ? screenWidth / 12 : 45, | |
color: textColor, | |
), | |
), | |
SelectableText( | |
unit, | |
style: TextStyle( | |
fontWeight: FontWeight.w300, | |
fontSize: 15, | |
color: textColor, | |
), | |
), | |
], | |
), | |
SelectableText( | |
name, | |
style: TextStyle( | |
fontSize: screenWidth < 760 ? screenWidth / 20 : 25, | |
color: Colors.white, | |
), | |
), | |
], | |
), | |
), | |
), | |
); | |
} | |
// This method is rerun every time "setState" is called | |
@override | |
Widget build(BuildContext context) { | |
// 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. | |
var screenHeight = MediaQuery.of(context).size.height; | |
return Scaffold( | |
backgroundColor: Colors.green[200], | |
appBar: AppBar( | |
backgroundColor: Colors.transparent, | |
elevation: 0, | |
actions: [ | |
IconButton( | |
onPressed: () {}, | |
icon: const Icon( | |
Icons.sunny, | |
color: Colors.black, | |
), | |
), | |
], | |
title: SelectableText( | |
widget.appTitle, | |
style: TextStyle( | |
color: Colors.green[900], | |
fontWeight: FontWeight.w600, | |
), // This trailing comma makes auto-formatting nicer for build methods. | |
), | |
), | |
body: FutureBuilder<WeatherModel>( | |
future: getData(), | |
builder: | |
(BuildContext context, AsyncSnapshot<WeatherModel> snapshot) { | |
if (snapshot.connectionState == ConnectionState.done) { | |
// 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). | |
return Column( | |
children: [ | |
SizedBox( | |
height: screenHeight < 760 ? screenHeight / 4 : 250, | |
width: double.infinity, | |
child: Stack( | |
alignment: Alignment.bottomCenter, | |
children: [ | |
Image.network( | |
'https://openweathermap.org/img/wn/${data!.icon}@4x.png', | |
), | |
SelectableText( | |
data!.cityName.toString(), | |
style: TextStyle( | |
fontSize: 25, | |
fontWeight: FontWeight.bold, | |
color: Colors.brown[700], | |
), | |
), | |
], | |
), | |
), | |
const SizedBox(height: 8.0), | |
SelectableText( | |
'${data!.temp}° C', // F | |
style: TextStyle( | |
fontSize: 50, | |
fontWeight: FontWeight.bold, | |
color: Colors.green[900], | |
), | |
), | |
const SizedBox(height: 15.0), | |
SelectableText( | |
data!.weatherDescription!, | |
style: TextStyle( | |
fontSize: 18, | |
fontWeight: FontWeight.bold, | |
color: Colors.green[900], | |
), | |
), | |
const SizedBox(height: 10.0), | |
Wrap( | |
crossAxisAlignment: WrapCrossAlignment.center, | |
children: [ | |
Image.network( | |
'https://openweathermap.org/img/wn/01d.png'), | |
SelectableText( | |
'${DateTime.fromMillisecondsSinceEpoch(data!.sunrise! * 1000).toString().substring(11, 19)} |', | |
style: TextStyle( | |
fontSize: 16, | |
fontWeight: FontWeight.bold, | |
color: Colors.green[900], | |
), | |
), | |
Image.network( | |
'https://openweathermap.org/img/wn/01n.png'), | |
SelectableText( | |
DateTime.fromMillisecondsSinceEpoch( | |
data!.sunset! * 1000) | |
.toString() | |
.substring(11, 19), | |
style: TextStyle( | |
fontSize: 16, | |
fontWeight: FontWeight.bold, | |
color: Colors.green[900], | |
), | |
), | |
], | |
), | |
const Spacer(), | |
SizedBox( | |
height: 200, | |
width: 760, | |
child: Padding( | |
padding: const EdgeInsets.all(10.0), | |
child: Row( | |
mainAxisAlignment: MainAxisAlignment.spaceEvenly, | |
children: [ | |
_detailCard( | |
'Wind', | |
data!.wind.toString(), | |
'km', | |
context, | |
), // mph | |
_detailCard( | |
'Humidity', | |
data!.humidity.toString(), | |
'%', | |
context, | |
), | |
_detailCard( | |
'Pressure', | |
data!.pressure.toString(), | |
'mBar', | |
context, | |
), // inHg | |
// _detailCard( | |
// 'Feels like', | |
// data!.feelsLike.toString(), | |
// '° C', | |
// context, | |
// ), // ° F | |
], | |
), | |
), | |
), | |
const SizedBox(height: 20.0), | |
], | |
); | |
} else if (snapshot.connectionState == ConnectionState.waiting) { | |
return const CircularProgressIndicator(); | |
} | |
return Container(); | |
}, | |
)); | |
} | |
} | |
// A Material theme app widget | |
class MyApp extends StatelessWidget { | |
final String appTitle; | |
const MyApp({ | |
Key? key, | |
required this.appTitle, | |
}) : super(key: key); | |
// This widget is the root of your application. | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
debugShowCheckedModeBanner: false, | |
title: appTitle, | |
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: WeatherApp(appTitle: appTitle), | |
); | |
} | |
} | |
// Dart main class | |
void main() async { | |
WidgetsFlutterBinding.ensureInitialized(); | |
runApp(const MyApp(appTitle: 'Weather App')); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment