Aplikasi Cuaca: Coba Lagi
Termasuk bagian Data dan Jaringan dalam perjalanan Flutter di Coddy. Pelajaran 26 dari 27.
Aplikasi cuaca dibuka di kereta, terowongan, dan pegunungan. Tanpa sinyal, aplikasi harus menjelaskannya dengan gamblang dan menawarkan cara untuk kembali, bukan menampilkan indikator pemuatan yang tidak pernah berhenti atau sebaris teks error.
Tampilkan error di layarnya sendiri yang sederhana: ikon, kalimat yang mudah dipahami, detail di bawahnya, dan tombol Coba lagi yang membuat future baru di setState. Letakkan di method tersendiri, buildError, di samping buildWeather, agar build tetap singkat.
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (snapshot.hasError) {
return buildError(snapshot.error!);
}
return buildWeather(snapshot.data!);Memeriksa ConnectionState.waiting terlebih dahulu akan menampilkan indikator pemuatan selama setiap pemuatan, termasuk percobaan kedua. Setelah future selesai, yang ada hanyalah error atau data, jadi baris terakhir dapat menggunakan snapshot.data!.
Untuk melihatnya di pratinjau, tulis offline = true; sebagai baris pertama main, lalu tekan Jalankan. Hapus sebelum menekan Periksa.
Tantangan
SedangTambahkan layar error ke aplikasi:
- method
buildError(Object error):Icons.cloud_off(ukuran 64),Could not load the weather(ukuran 20), teks error, dan tombol Try again - method
tryAgainyang menetapkanloadWeather()baru di dalamsetState - di builder, periksa
ConnectionState.waitingterlebih dahulu, lalu error, kemudian tampilkan cuaca
Coba sendiri
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'api.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: WeatherScreen(),
);
}
}
class WeatherScreen extends StatefulWidget {
const WeatherScreen({super.key});
@override
State<WeatherScreen> createState() => _WeatherScreenState();
}
class _WeatherScreenState extends State<WeatherScreen> {
late Future<Weather> _weather;
@override
void initState() {
super.initState();
_weather = loadWeather();
}
Future<Weather> loadWeather() async {
final body = await fetch('/weather/lisbon');
return Weather.fromJson(jsonDecode(body));
}
IconData iconFor(String condition) {
return switch (condition) {
'Sunny' => Icons.wb_sunny,
'Cloudy' => Icons.cloud,
'Rainy' => Icons.umbrella,
'Snowy' => Icons.ac_unit,
_ => Icons.help_outline,
};
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Weather')),
body: Center(
child: FutureBuilder<Weather>(
future: _weather,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Could not load: ${snapshot.error}');
}
if (snapshot.hasData) {
return buildWeather(snapshot.data!);
}
return const CircularProgressIndicator();
},
),
),
);
}
Widget buildWeather(Weather weather) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(weather.city, style: const TextStyle(fontSize: 32)),
Icon(iconFor(weather.condition), size: 96, color: Colors.orange),
Text(
'${weather.temperature}°',
style: const TextStyle(fontSize: 72, fontWeight: FontWeight.bold),
),
Text(weather.condition, style: const TextStyle(fontSize: 24)),
const SizedBox(height: 32),
for (final day in weather.forecast)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 8),
child: Row(
children: [
Icon(iconFor(day.condition)),
const SizedBox(width: 16),
Expanded(
child: Text(
DateFormat('EEEE').format(day.date),
style: const TextStyle(fontSize: 18),
),
),
Text('${day.high}° / ${day.low}°', style: const TextStyle(fontSize: 18)),
],
),
),
],
);
}
}
class Weather {
Weather({
required this.city,
required this.temperature,
required this.condition,
required this.forecast,
});
final String city;
final int temperature;
final String condition;
final List<DayForecast> forecast;
factory Weather.fromJson(Map<String, dynamic> json) {
final days = json['forecast'] as List<dynamic>;
return Weather(
city: json['city'] as String,
temperature: json['temperature'] as int,
condition: json['condition'] as String,
forecast: days
.map((day) => DayForecast.fromJson(day as Map<String, dynamic>))
.toList(),
);
}
}
class DayForecast {
DayForecast({
required this.date,
required this.condition,
required this.high,
required this.low,
});
final DateTime date;
final String condition;
final int high;
final int low;
factory DayForecast.fromJson(Map<String, dynamic> json) {
return DayForecast(
date: DateTime.parse(json['date'] as String),
condition: json['condition'] as String,
high: json['high'] as int,
low: json['low'] as int,
);
}
}
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Data dan Jaringan
3Saat Pemanggilan Gagal
Menangkap Error dengan tryMelempar ErrorMembersihkan dengan finallyStatus Error FutureBuilderTombol Coba Lagi6Proyek: Aplikasi Cuaca
Aplikasi Cuaca: Saat IniAplikasi Cuaca: Tiga Hari BerikutnyaLatihan sendiri: Compiler Flutter online