Aplikasi Cuaca: Pilih Kota
Termasuk bagian Data dan Jaringan dalam perjalanan Flutter di Coddy. Pelajaran 27 dari 27.
Fitur terakhir: sederet kota di bagian atas, Lisbon, Tokyo, dan Oslo. Ketuk salah satunya untuk memuat cuacanya.
Memilih kota berarti memuat ulang dengan path yang berbeda. Simpan city yang dipilih di state, beri loadWeather parameter, dan susun path darinya:
Future<Weather> loadWeather(String city) async {
final body = await fetch('/weather/${city.toLowerCase()}');
return Weather.fromJson(jsonDecode(body));
}Tombolnya bertuliskan Tokyo, tetapi path di server adalah /weather/tokyo. toLowerCase() mengubah yang satu menjadi yang lain. Path di sebagian besar server membedakan huruf besar dan kecil, jadi /weather/Tokyo menghasilkan Not found.
Letakkan deretan tombol di luar FutureBuilder, agar tetap terlihat di layar saat sebuah kota dimuat. Gunakan Column dengan deretan tombol di bagian atas dan FutureBuilder di dalam Expanded di bawahnya.
Tantangan
SulitTambahkan pemilih kota:
String city = 'Lisbon';di state, danloadWeather(String city)mengambil/weather/ditambah nama kota dalam huruf kecil- di bagian atas body,
Rowberisi tigaTextButtonyang diratakan ke tengah:Lisbon,Tokyo,Oslo - mengetuk salah satunya akan menetapkan
citydan_weatherbaru di dalamsetState - di bawah row, letakkan
FutureBuilderdi dalamExpanded, lalu **Coba lagi** memuat ulang kota yang dipilih
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));
}
void tryAgain() {
setState(() {
_weather = loadWeather();
});
}
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.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (snapshot.hasError) {
return buildError(snapshot.error!);
}
return buildWeather(snapshot.data!);
},
),
),
);
}
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)),
],
),
),
],
);
}
Widget buildError(Object error) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.cloud_off, size: 64),
const SizedBox(height: 16),
const Text('Could not load the weather', style: TextStyle(fontSize: 20)),
Text('$error'),
const SizedBox(height: 16),
ElevatedButton(onPressed: tryAgain, child: const Text('Try again')),
],
);
}
}
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