Weather Forecast ¶
This example demonstrates how to create a Flutter application that utilizes the
gem_kit
package to display a weather forecast on a map. The application initializes the GemKit SDK and provides a user interface to show current, hourly, and daily weather forecasts.
|
Setup ¶
Prerequisites ¶
Build and Run ¶
Navigate to the project folder for this example to build and run the application.
Note - the
gem_kit
directory containing the Maps SDK for Flutter
should be in the
plugins
directory of the example, e.g.
example_pathname/plugins/gem_kit
- see the environment setup guide above.
Run:
flutter
pub
get
Configure the native parts:
First, verify that the
ANDROID_SDK_ROOT
environment variable
is set to the root path of your android SDK.
In
android/build.gradle
add the
maven
block as shown,
within the
allprojects
block, for both debug and release builds:
allprojects {
repositories {
google()
mavenCentral()
maven {
url "${rootDir}/../plugins/gem_kit/android/build"
}
}
}
in
android/app/build.gradle
within the
android
block, in the
defaultConfig
block,
the android SDK version
minSdk
must be set as shown below.
Additionally, for release builds, in
android/app/build.gradle
,
within the
android
block, add the
buildTypes
block as shown:
Replace
example_pathname
with the actual project pathname
android {
defaultConfig {
applicationId "com.magiclane.gem_kit.examples.example_pathname"
minSdk 21
targetSdk flutter.targetSdk
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
minifyEnabled false
shrinkResources false
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
Then run the project:
flutter run --debug
orflutter run --release
App entry and initialization ¶
const projectApiToken = String.fromEnvironment('GEM_TOKEN');
void main() {
runApp(const MyApp());
}
This code initializes the projectApiToken with the required authorization token and launches the app.
How It Works ¶
-
Main App Setup
: The main app initializes GemKit and displays a map. -
Weather Forecast Page
: Users can navigate through current, hourly and daily forecasts for a specific location.
User Interface ¶
The main application consists of a simple user interface that displays a map along with a weather forecast option. The user can tap on the weather icon in the app bar to navigate to a detailed weather forecast page.
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Weather Forecast',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
void dispose() {
GemKit.release();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepPurple[900],
title: const Text('Weather Forecast', style: TextStyle(color: Colors.white)),
actions: [
IconButton(
onPressed: () => _onWeatherForecastTap(context),
icon: Icon(
Icons.sunny,
color: Colors.white,
),
),
],
),
body: GemMap(appAuthorization: projectApiToken),
);
}
void _onWeatherForecastTap(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute<dynamic>(
builder: (context) => WeatherForecastPage(),
));
}
}
This code sets up the main application UI, including an app bar and a body that contains a map.
|
Weather Forecast Page ¶
The
WeatherForecastPage
displays the weather forecast options, allowing users to switch between current, hourly, and daily forecasts. Below is the implementation of the
WeatherForecastPage
.
class WeatherForecastPage extends StatefulWidget {
const WeatherForecastPage({super.key});
@override
State<WeatherForecastPage> createState() => _WeatherForecastPageState();
}
class _WeatherForecastPageState extends State<WeatherForecastPage> {
WeatherTab _weatherTab = WeatherTab.now;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: true,
foregroundColor: Colors.white,
title: const Text("Weather Forecast", style: TextStyle(color: Colors.white)),
backgroundColor: Colors.deepPurple[900],
),
body: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
children: [
// Tab buttons for 'Now', 'Hourly', and 'Daily' forecasts
SizedBox(
height: 40.0,
child: Row(
children: [
Expanded(
child: InkWell(
child: Center(child: Text("Now")),
onTap: () => setState(() {
_weatherTab = WeatherTab.now;
}),
),
),
Expanded(
child: InkWell(
child: Center(child: Text("Hourly")),
onTap: () => setState(() {
_weatherTab = WeatherTab.hourly;
}),
),
),
Expanded(
child: InkWell(
child: Center(child: Text("Daily")),
onTap: () => setState(() {
_weatherTab = WeatherTab.daily;
}),
),
),
],
),
),
// Display the selected forecast page
Expanded(
child: Builder(
builder: (context) {
if (_weatherTab == WeatherTab.now) {
return _buildCurrentForecast();
} else if (_weatherTab == WeatherTab.hourly) {
return _buildHourlyForecast();
} else if (_weatherTab == WeatherTab.daily) {
return _buildDailyForecast();
}
return Container();
},
),
),
],
),
),
);
}
FutureBuilder<LocationForecast> _buildCurrentForecast() {
return FutureBuilder(
future: _getCurrentForecast(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (!snapshot.hasData) return Center(child: Text("Error loading current forecast."));
return ForecastNowPage(condition: snapshot.data!, landmarkName: "Paris");
},
);
}
FutureBuilder<List<LocationForecast>> _buildHourlyForecast() {
return FutureBuilder(
future: _getHourlyForecast(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (!snapshot.hasData) return Center(child: Text("Error loading hourly forecast."));
return ForecastHourlyPage(locationForecasts: snapshot.data!);
},
);
}
FutureBuilder<List<LocationForecast>> _buildDailyForecast() {
return FutureBuilder(
future: _getDailyForecast(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (!snapshot.hasData) return Center(child: Text("Error loading daily forecast."));
return ForecastDailyPage(locationForecasts: snapshot.data!);
},
);
}
}
This code implements the
WeatherForecastPage
, which manages the state of the currently selected weather tab and displays the appropriate forecast information.
|
Getting Current, Hourly and Daily Forecasts ¶
The following methods retrieve hourly and daily forecasts from the GemKit SDK.
Future<LocationForecast> _getCurrentForecast() async {
final locationCoordinates = Coordinates(latitude: 48.864716, longitude: 2.349014);
final weatherCurrentCompleter = Completer<List<LocationForecast>?>();
WeatherService.getCurrent(
coords: [locationCoordinates],
onCompleteCallback: (err, result) async {
weatherCurrentCompleter.complete(result);
});
final currentForecast = await weatherCurrentCompleter.future;
return currentForecast!.first;
}
Future<List<LocationForecast>> _getHourlyForecast() async {
final locationCoordinates = Coordinates(latitude: 48.864716, longitude: 2.349014);
final weatherHourlyCompleter = Completer<List<LocationForecast>?>();
WeatherService.getHourlyForecast(
hours: 24,
coords: [locationCoordinates],
onCompleteCallback: (err, result) async {
weatherHourlyCompleter.complete(result);
},
);
final currentForecast = await weatherHourlyCompleter.future;
return currentForecast!;
}
Future<List<LocationForecast>> _getDailyForecast() async {
final locationCoordinates = Coordinates(latitude: 48.864716, longitude: 2.349014);
final weatherDailyCompleter = Completer<List<LocationForecast>?>();
WeatherService.getDailyForecast(
days: 10,
coords: [locationCoordinates],
onCompleteCallback: (err, result) async {
weatherDailyCompleter.complete(result);
},
);
final currentForecast = await weatherDailyCompleter.future;
return currentForecast!;
}
These methods use the
WeatherService
to fetch current, hourly and daily weather data for a specified location.
Hourly Forecast Page ¶
The
ForecastHourlyPage
displays the hourly weather forecasts. Below is the implementation of this page.
class ForecastHourlyPage extends StatelessWidget {
final List<LocationForecast> locationForecasts;
const ForecastHourlyPage({super.key, required this.locationForecasts});
@override
Widget build(BuildContext context) {
return SizedBox(
height: MediaQuery.of(context).size.height * 0.8,
child: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: locationForecasts.length,
itemBuilder: (context, index) {
return ListTile(
title: Text("Hour ${index + 1}: ${locationForecasts[index].temperature} °C"),
subtitle: Text(locationForecasts[index].condition),
);
},
),
),
],
),
);
}
}
This code defines the
ForecastHourlyPage
, which presents a list of hourly weather forecasts for the user.
|
Daily Forecast Page ¶
The
ForecastDailyPage
displays the daily weather forecasts. Below is the implementation of this page.
class ForecastDailyPage extends StatelessWidget {
final List<LocationForecast> locationForecasts;
const ForecastDailyPage({super.key, required this.locationForecasts});
@override
Widget build(BuildContext context) {
return SizedBox(
height: MediaQuery.of(context).size.height * 0.8,
child: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: locationForecasts.length,
itemBuilder: (context, index) {
return ListTile(
title: Text("Day ${index + 1}: ${locationForecasts[index].temperature} °C"),
subtitle: Text(locationForecasts[index].condition),
);
},
),
),
],
),
);
}
}
This code defines the
ForecastDailyPage
, which presents a list of daily weather forecasts for the user.
|