OpenWeather APIOpenWeather và API của nó đóng vai trò quan trọng trong việc cung cấp thông tin thờitiết chính xác và đáng tin cậy cho các nhà phát triển và ứng dụng trên toàn thế giới.Op
XÂY DỰNG ỨNG DỤNG
Model
3.1.1 Hour_Forecast_model.dart class Hourly_Model {
Hourly_Model({ required this.hourlydt, required this.hourlytemp, required this.hourlyicon});
Map toJson() { return {
} factory Hourly_Model.fromJson(dynamic json) { return Hourly_Model( hourlydt: [ json["list"][0]["dt"].toString(), json["list"][1]["dt"].toString(), json["list"][2]["dt"].toString(), json["list"][3]["dt"].toString(), json["list"][4]["dt"].toString(), json["list"][5]["dt"].toString(), json["list"][6]["dt"].toString(),
] as List, hourlytemp: [ json["list"][0]["main"]["temp"].toString(), json["list"][1]["main"]["temp"].toString(), json["list"][2]["main"]["temp"].toString(), json["list"][3]["main"]["temp"].toString(), json["list"][4]["main"]["temp"].toString(), json["list"][5]["main"]["temp"].toString(), json["list"][6]["main"]["temp"].toString()
] as List, hourlyicon: [ json["list"][0]["weather"][0]["icon"].toString(), json["list"][1]["weather"][0]["icon"].toString(), json["list"][2]["weather"][0]["icon"].toString(), json["list"][3]["weather"][0]["icon"].toString(), json["list"][4]["weather"][0]["icon"].toString(), json["list"][5]["weather"][0]["icon"].toString(), json["list"][6]["weather"][0]["icon"].toString(),
-Trong model ‘Hour_Forecast_model.dart’ chứa lớp Hourly_Model, mô tả thông tin về dự báo thời tiết theo giờ Lớp này có ba thuộc tính chính là hourlydt, hourlytemp, và hourlyicon Chúng lưu trữ các giá trị tương ứng về thời gian, nhiệt độ và biểu tượng thời tiết cho từng khoảng thời gian trong ngày.
-Phương thức khởi tạo Hourly_Model được sử dụng để tạo ra một đối tượng mới của lớp này Nó nhận vào ba tham số bắt buộc hourlydt, hourlytemp, và hourlyicon, và gán chúng cho các thuộc tính tương ứng của đối tượng.
-Phương thức toJson được sử dụng để chuyển đổi đối tượng Hourly_Model thành một đối tượng Map Đối tượng Map này sẽ chứa thông tin về thời gian, nhiệt độ và biểu tượng thời tiết theo giờ Khi gọi phương thức này, đối tượng Map được tạo và các thuộc tính của đối tượng Hourly_Model sẽ được thêm vào đối tượng Map Sau đó, đối tượng Map sẽ được trả về.
Phương thức fromJson là một hàm tạo đặc biệt để tạo đối tượng Hourly_Model từ dữ liệu JSON Hàm này nhận một đối số là json, đại diện cho dữ liệu JSON chưa xác định kiểu dữ liệu Dựa trên cấu trúc của dữ liệu JSON, các giá trị tương ứng với thời gian, nhiệt độ
Downloaded by Quang Tr?n (tranquang141994@gmail.com) và biểu tượng thời tiết được trích và gán cho các thuộc tính tương ứng của đối tượng Hourly_Model Cuối cùng, đối tượng Hourly_Model sẽ được tạo và trả về.
3.1.2 Weather_model.dart class weatherModel { final String? cityName; final double temp; final String? condition; final double windspeed; final int humidity; final int pressure; final double clouds; final String? icon_id; weatherModel({ required this.cityName, required this.temp, required this.condition, required this.windspeed, required this.humidity, required this.pressure, required this.clouds, required this.icon_id,
Map toJson() { return {
'icon_id': this.icon_id,
} factory weatherModel.fromJson(dynamic json) { return weatherModel( cityName: json["name"].toString(), temp: json["main"]["temp"].toDouble(), condition: json["weather"][0]
["description"].toString().toUpperCase(), windspeed: json["wind"]["speed"].toDouble(), humidity: json["main"]["humidity"].toInt(), pressure: json["main"]["pressure"].toInt(), clouds: json["clouds"]["all"].toDouble(), icon_id: json["weather"][0]["icon"].toString(),
Downloaded by Quang Tr?n (tranquang141994@gmail.com)
-Lớp weatherModel trong đoạn mã trên là một mô hình dữ liệu để đại diện cho thông tin về thời tiết của một thành phố cụ thể Nó bao gồm các thuộc tính như cityName, temp, condition, windspeed, humidity, pressure, clouds, và icon_id.
-Phương thức toJson được sử dụng để chuyển đổi đối tượng weatherModel thành một đối tượng Map có kiểu dữ liệu gồm String là khóa và dynamic là giá trị Trong phương thức này, ta tạo ra một đối tượng Map mới và gán các thuộc tính của đối tượng weatherModel cho các khóa tương ứng trong đối tượng Map Kết quả là phương thức toJson trả về một đối tượng Map chứa thông tin về các thuộc tính của đối tượng weatherModel.
-Phương thức fromJson được sử dụng để tạo một đối tượng weatherModel từ một đối tượng dynamic có kiểu dữ liệu là JSON Trong phương thức này, ta trích xuất thông tin từ JSON và gán cho các thuộc tính tương ứng của đối tượng weatherModel Sau đó, ta tạo và trả về một đối tượng weatherModel mới với các giá trị thuộc tính đã được gán.
Page
Widget build(BuildContext context) { weatherData = Provider of (context).weatherData; weatherData2 = Provider of (context).weatherData2; return Scaffold( body: Provider of (context).weatherData == null &&
Provider of (context).weatherData2 == null
- Trong hàm build(), trước khi hiển thị giao diện thời tiết, trang chủ kiểm tra xem liệu dữ liệu thời tiết có sẵn trong Provider hay không bằng cách kiểm tra xem các thuộc tính weatherData và weatherData2 có khác null hay không.
Nếu không có dữ liệu thời tiết, trang chủ sẽ hiển thị trang tìm kiếm bằng cách gọi widget SearchPage() Nếu có dữ liệu thời tiết, trang chủ sẽ hiển thị giao diện thời tiết bằng cách sử dụng một Container widget.
-Code giao diện hiển thị thời tiết:
Container( child: SafeArea( child: ListView( children: [
Container( decoration: BoxDecoration( image: DecorationImage(
Downloaded by Quang Tr?n (tranquang141994@gmail.com) colorFilter: ColorFilter.mode(
Colors black38 , BlendMode.darken), image: AssetImage(
'assets/cloud-in-blue-sky.jpg',
), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(25), bottomRight: Radius.circular(25),
), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end, children: [
Container( child: AppBar( backgroundColor: Colors.transparent, elevation: 0, leading: IconButton( icon: Icon(
Icons.menu, color: Colors.white,
MaterialPageRoute( builder: (context) { return SearchPage( updateUi: updateUi,
Padding( padding: const EdgeInsets.only(top: 200.0), child: Align( alignment: Alignment(0.0, 1.0), child: SizedBox( height: 10, width: 10, child: OverflowBox( maxWidth: MediaQuery of (context).size.width, maxHeight:
(MediaQuery of (context).size.height / 3), child: Stack( children: [
Container( padding: EdgeInsets.symmetric( horizontal: 20), width: double.infinity, height: double.infinity, child: Card( color: Colors.white, elevation: 5,
Downloaded by Quang Tr?n (tranquang141994@gmail.com) shape: RoundedRectangleBorder( borderRadius:
Container( padding: EdgeInsets.only( top: 15, left: 20, right: 20), child: Column( crossAxisAlignment:
'${weatherData!.cityName}' toUpperCase(), style: TextStyle( fontSize: 24, color: Colors
now()), style: TextStyle( fontSize: 16, color: Colors
Container( padding: EdgeInsets.only( left: 35), child: Column( children: [
Downloaded by Quang Tr?n (tranquang141994@gmail.com) color: Colors blueAccent , fontSize: 30, fontWeight:
{(weatherData!.temp.toString())}\u2103', style: TextStyle( fontSize: 16, color: Colors black45, fontWeight:
Container( padding: EdgeInsets.only( right: 20), child: Column( mainAxisAlignment:
SizedBox( width: 120, height: 120, child: Image.asset( "assets/weather/$ {weatherData!.icon_id}.png"),
Padding( padding: const EdgeInsets.only( top: 10.0), child: Column( crossAxisAlignment:
start, children: [ currentWeatherDetails( weatherData!.windspeed toString(), weatherData!.clouds toString(), weatherData!.humidity toString()),
Downloaded by Quang Tr?n (tranquang141994@gmail.com)
Padding( padding: const EdgeInsets.only(top: 200.0, left: 20.0), child: Align( alignment: Alignment topLeft , child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [
"Today".toUpperCase(), style: TextStyle( fontSize: 16, color: Colors.black45, fontWeight: FontWeight.bold),
SizedBox( height: 170, width: MediaQuery of (context).size.width - 40, child: ListView.builder( scrollDirection: Axis.horizontal, itemCount: 5, itemBuilder: (BuildContext context, index) { return HourlyWidget(
"${weatherData2!.hourlytemp?[index]}", "${weatherData2!.hourlyicon?[index]}", "${weatherData2?.hourlydt?[index]}", index,
Container( margin: const EdgeInsets.all(12), height: 350, padding: const EdgeInsets.all(15), decoration: BoxDecoration( color: Colors deepPurple [50], borderRadius: BorderRadius.circular(20),
"Forecast for 7 days :", style: TextStyle( fontSize: 20.0, fontWeight: FontWeight.w500, letterSpacing: 0.5, color: Colors.black,
Downloaded by Quang Tr?n (tranquang141994@gmail.com) height: 30,
SizedBox( height: 250, width: 350, child: ListView.builder( itemCount: 5, itemBuilder:
(BuildContext context, index) { return DailyForcast(
"${weatherData2?.hourlyicon?[index]}", "${weatherData2?.hourlytemp?[index]}", );
Trang chủ được chia thành hai phần chính Nếu dữ liệu thời tiết chưa được cung cấp (Provider.of(context).weatherData==nullvàProvider.of(context).weatherData2 == null), thì trang chủ sẽ hiển thị SearchPage để người dùng có thể tìm kiếm thông tin thời tiết cho một thành phố cụ thể.
Ngược lại, nếu dữ liệu thời tiết đã được cung cấp, trang chủ sẽ hiển thị một Container chứa các thành phần hiển thị thông tin thời tiết.
Trong Container, có một SafeArea để đảm bảo hiển thị an toàn trên mọi thiết bị Bên trong SafeArea, có một ListView để chứa các thành phần con khác.
Các thành phần con bao gồm:
Phần tiêu đề và nút menu: Được hiển thị bên trên cùng của trang chủ Nút menu không có hành động cụ thể được xử lý trong mã này.
Phần hiển thị thông tin thời tiết hiện tại: Hiển thị tên thành phố, ngày hiện tại, điều kiện thời tiết, nhiệt độ và biểu tượng thời tiết Thông tin này được lấy từ weatherData thông qua Provider.
Chi tiết thời tiết hiện tại: Hiển thị thông tin về tốc độ gió, độ ẩm và độ che phủ mây. Thông tin này được truyền vào hàm currentWeatherDetails và hiển thị trong một widget.
Downloaded by Quang Tr?n (tranquang141994@gmail.com)
Dự báo theo giờ: Hiển thị thông tin thời tiết dự báo theo giờ cho hôm nay Mỗi mục được hiển thị bằng một widget HourlyWidget, hiển thị nhiệt độ, biểu tượng và thời gian.
Dự báo trong 7 ngày: Hiển thị thông tin dự báo thời tiết cho 7 ngày tiếp theo Mỗi mục được hiển thị bằng một widget DailyForcast, hiển thị biểu tượng, nhiệt độ và ngày.
3.2.2 SearchPage.dart import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:weatherforecast/Models/Hour_Forecast_model.dart'; import ' /Models/Weather_model.dart'; import ' /Provider/WeatherProvider.dart'; import ' /Services/Weather_API_Service.dart';
String? cityName ; class SearchPage extends StatelessWidget {
Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Search a City"),
), body: SafeArea( child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [
Center( child: SizedBox( width: 300.0, height: 100.0, child: Padding( padding: const EdgeInsets.only(top: 10.0), child: Column( children: [
), onSubmitted: (data) async { cityName = data;
WeatherService Service = WeatherService(); weatherModel weather1 = await Service.getWeather(CityName: cityName!); Hourly_Model weather2 =
(await Service.getHourlyWeather(CityName: cityName!)) as Hourly_Model;
Provider.of(context, listen: false)
Provider of (context, listen: false)
Downloaded by Quang Tr?n (tranquang141994@gmail.com)
}, decoration: InputDecoration( contentPadding: EdgeInsets.symmetric( vertical: 10, horizontal: 20), suffixIcon: GestureDetector( onTap: () async {
WeatherService Service = WeatherService(); weatherModel weather1 = await Service.getWeather(CityName: cityName!);
(await Service.getHourlyWeather(CityName: cityName!)) as Hourly_Model;
Provider of (context, listen: false)
Provider.of(context, listen: false)
}, child: Icon(Icons.search)), hintText: "Nhâ ̣p đ椃⌀a điऀm", border: OutlineInputBorder( borderRadius: BorderRadius.circular(30.0),
-Các đối số của SearchPage là updateUi, một VoidCallback được sử dụng để cập nhật giao diện người dùng.
Trong build method, được định nghĩa một Scaffold để tạo ra một trang có tiêu đề "Search a City" Phần thân (body) của Scaffold bao gồm một SafeArea chứa một cột (Column) chính.
Trong cột chính, có một Center widget để căn giữa các thành phần bên trong Bên trong Center, có một SizedBox với chiều rộng 300.0 và chiều cao 100.0 để giới hạn kích thước của thành phần.
Bên trong SizedBox, có một Padding để thêm khoảng cách phía trên cho thành phần. Trong Padding, có một Column để chứa các thành phần con khác.
Downloaded by Quang Tr?n (tranquang141994@gmail.com)
Trong Column, có một TextField để người dùng nhập tên thành phố Khi người dùng nhấn Enter hoặc submit dữ liệu, một số xử lý xảy ra Tên thành phố được gán cho biến cityName, sau đó một dịch vụ WeatherService được sử dụng để lấy thông tin thời tiết cho thành phố đó Dữ liệu thời tiết được gán cho một weatherModel và một Hourly_Model thông qua việc sử dụng dịch vụ getWeather và getHourlyWeather Sau đó, thông tin thời tiết được cập nhật thông qua Provider và giao diện người dùng được cập nhật bằng cách gọi maybePop trên Navigator để quay lại trang trước đó.
Thêm vào đó, có một biểu tượng tìm kiếm (Icon(Icons.search)) để người dùng có thể nhấp vào để tìm kiếm thông tin thời tiết cho thành phố đã nhập.
Trong mã, Provider.of(context, listen: false) được sử dụng để truy cập đối tượng WeatherProvider thông qua Provider để cung cấp dữ liệu thời tiết cho ứng dụng.
Downloaded by Quang Tr?n (tranquang141994@gmail.com)
Provider
3.3.1 WeatherProvider.dart class WeatherProvider extends ChangeNo琀椀昀椀er { weatherModel? _weatherData;
Hourly_Model? _weatherData2; set weatherData(weatherModel? weather)
_weatherData=weather; no琀椀fyListeners();
} set weatherData2(Hourly_Model? weather)
_weatherData2=weather; no琀椀fyListeners();
Hourly_Model? get weatherData2 => _weatherData2;
} Đây là một phần trong một ứng dụng để xử lý việc thay đổi dữ liê ̣u khi dữ liê ̣u từ API thay đổi Lớp này kế thừa từ ChangeNotifier, cho phép nó thông báo cho các hàm khác về sự thay đổi trong trạng thái tìm kiếm địa điểm coi thời tiết thông qua phương thức notifyListeners().
_weatherData và _weatherData2 để lưu trữ thông tin thời tiết Ban đầu, nó được đặt là null.
Getter weatherData và weatherData2 để truy cập giá trị của _weatherData và _weatherData2
Setter weatherData và weatherData2 để gán giá trị vào _weatherData và _weatherData2
Gửi thông báo (notifyListeners) cho các hàm rằng có sự thay đổi trong trạng thái dữ liê ̣u từ API thay đổi.
3.4.1 Weather_API_Service.dart class WeatherService {
String baseUrl = 'https://api.openweathermap.org/data/2.5';
String apiKey = '7ab1eb8717ba67e35f45c799a0a5d8d8';
Future getWeather({ required String CityName}) async {
'$baseUrl/weather?q=$CityName&units=metric&lang=vi&appid=$apiKey');
Downloaded by Quang Tr?n (tranquang141994@gmail.com) http.Response response = await http.get(url);
Map data = jsonDecode(response.body); return weatherModel.fromJson(data);
Future getHourlyWeather({required String CityName}) async{ Uri url = Uri parse (
'$baseUrl/forecast? q=$cityName&units=metric&lang=vi&appid=$apiKey&cnt=7'); http.Response response = await http.get(url);
Map data1 = jsonDecode(response.body); return Hourly_Model.fromJson(data1);
Lớp Weather_API_Service được dùng để tạo requests HTTP openWeather API và trả về các giá trị của hai lớp weatherModel và Hourly_Model
Hàm getWeather và getHourly Weather nhận một địa điểm CityName làm đối số và trả về một đối tượng Future chứa dữ liệu thời tiết cho địa điểm đó Khi được gọi, hàm sẽ thực hiê ̣n các bước sau:
• Gọi url và truy câ ̣p tới đường d̀n openWeather API.
• Sử dụng http.get(url) để lấy dữ liê ̣u từ openWeather API.
• Sau khi có được dữ liê ̣u response, ta dùng phương thức Map để gán các giá trị trả về JSON thành các model tương ứng bằng cách sử dụng hàm jsonDecode
• Phương thức fromJson nhận một Map JSON làm đối số và gán các giá trị của bản đồ cho các thuộc tính của các lớp Model.
Widget
Widget currentWeatherDetails(String wind, String clouds, String humidity){ return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
Column( mainAxisAlignment: MainAxisAlignment.center, children: [
Container( height: 60, width: 60, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors deepPurple [50],
Downloaded by Quang Tr?n (tranquang141994@gmail.com) borderRadius: BorderRadius.circular(12),
), child: Image.asset("assets/icons/windspeed.png"),
Container( height: 60, width: 60, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.deepPurple[50], borderRadius: BorderRadius.circular(12),
), child: Image.asset("assets/icons/clouds.png"),
Container( height: 60, width: 60, padding: const EdgeInsets.all(15), decoration: BoxDecoration( color: Colors deepPurple [50], borderRadius: BorderRadius.circular(12),
), child: Image.asset("assets/icons/humidity.png"),
Hàm currentWeatherDetails nhâ ̣n ba đối số và trả về mô ̣t Widget hiển các thông tin thời tiết như tốc đô ̣ gió, đô ̣ che phủ mây và đô ̣ ẩm. mainAxisAlignment: MainAxisAlignment.spaceEvenly dùng để phân bổ bố cục đều với khoảng cách bằng nhau giữa chúngtrên Row mainAxisAlignment: MainAxisAlignment.center dùng để căn giữa theo Column.
Trong Container có các thuộc tính như height, width, padding, decoration để điều chỉnh chiều cao, chiều rô ̣ng, khoảng cách, và kiểu dáng của Container.
Widget DailyForcast(String dt, String iconId, String temp) { return SizedBox( height: 70, child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly,
Downloaded by Quang Tr?n (tranquang141994@gmail.com) children: [
Container( height: 1, width: 250, color: Colors black ,
Container( padding: const EdgeInsets.all(10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.center, children: [
Text( getDay(int parse (dt)), style: TextStyle( fontSize: 18.0, fontWeight: FontWeight.w500, letterSpacing: 0.5, color: Colors.black45,
"assets/weather/$iconId.png", height: 40, width: 40,
"$temp \u2103", style: TextStyle( fontSize: 15.0, fontWeight: FontWeight w400 , letterSpacing: 0.5, color: Colors black ,
Hàm getDay trả về một chuỗi biểu diễn ngày tháng từ một số nguyên là số giây Hàm này sử dụng lớp DateTime để chuyển đổi số giây thành một đối tượng DateTime và sau đó sử dụng lớp DateFormat để định dạng ngày tháng theo kiểu ‘dd MMM’ (ví dụ: 01 Jan).
Hàm DailyForecast nhận ba đối số và trả về một Widget hiển thị các thông tin ngày tháng, biểu tượng thời tiết và nhiê ̣t đô ̣ thời tiết cho trong 4 ngày tiếp theo.
Hàm này sử dụng các Widget:
SizedBox: tạo ra một hộp có kích thước cố định cho widget con của SizedBox.
Column: sắp xếp các con của nó theo chiều dọc Trong Column, mainAxisAlignment: MainAxisAlignment.spaceEvenly dùng để phân bổ bố cục đều với khoảng cách bằng nhau giữa chúng trên Column
Downloaded by Quang Tr?n (tranquang141994@gmail.com)
Container: tạo ra một hộp chứa cho con của nó Nó có thể có các thuộc tính như height, width, padding, decoration để điều chỉnh kích thước, khoảng cách lề, và kiểu dáng của hộp chứa
Container được sử dụng để tạo ra các đường kẻ ngang có màu đen, và một hàng(widget Row) để chứa thông tin thời tiết trong 4 ngày tiếp theo gồm ngày tháng, biểu tượng thời tiết và nhiệt độ.
DateTime time = DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000);
String x = DateFormat('jm').format(time); return x;
Hàm này trả về một chuỗi biểu diễn thời gian từ một số nguyên là số giây Hàm này sử dụng lớp DateTime để chuyển đổi số giây thành một đối tượng DateTime và sau đó sử dụng lớp DateFormat để định dạng thời gian theo kiểu ‘jm’ (ví dụ: 12:00 PM).
Widget HourlyWidget(String temp, String iconId, String dt, int index){ return Container( margin: const EdgeInsets.all(12), height: 100, width: 100, decoration: BoxDecoration( color: index == 0
: Colors deepPurple [50], borderRadius: BorderRadius.circular(20),
), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [
Text( getTime( int parse (dt)), style: index == 0
? TextStyle( fontSize: 18.0, fontWeight: FontWeight w500 , letterSpacing: 0.5, color: Colors white ,)
: TextStyle( fontSize: 18.0, fontWeight: FontWeight w500 , letterSpacing: 0.5, color: Colors black ,),
Container( height: 60, width: 60, padding: const EdgeInsets.all(10), child: Image.asset("assets/weather/$iconId.png"),
Downloaded by Quang Tr?n (tranquang141994@gmail.com) style: index == 0
? TextStyle( fontSize: 18.0, fontWeight: FontWeight w500 , letterSpacing: 0.5, color: Colors white ,)
: TextStyle( fontSize: 18.0, fontWeight: FontWeight w500 , letterSpacing: 0.5, color: Colors black ,),
Hàm HourlyWidget nhận ba đối số và trả về một Widget hiển thị dự báo thời tiết gồm nhiê ̣t đô ̣, biểu tượng thời tiết, ngày giờ thời tiết cho một ngày cụ thể
Hàm này sử dụng các Widget sau:
SizedBox: một Widget để tạo ra một hộp chứa có kích thước cố định cho con của nó
Column: sắp xếp các con của nó theo chiều dọc Trong Column, mainAxisAlignment: MainAxisAlignment.spaceEvenly dùng để phân bổ bố cục đều với khoảng cách bằng nhau giữa chúngtrên Column
Container: tạo ra một hộp chứa cho con của nó Nó có thể có các thuộc tính như height, width, padding, decoration để điều chỉnh kích thước, khoảng cách lề, và kiểu dáng của hộp chứa
Container được sử dụng để tạo ra các đường kẻ ngang có màu đen, và một hàng(widget Row) để chứa thông tin thời tiết trong 4 ngày tiếp theo gồm ngày tháng, biểu tượng thời tiết và nhiệt độ.
Container được sử dụng để tạo ra các đường kẻ ngang có màu đen, và để chứa một hàng(Row) gồm ngày tháng, biểu tượng thời tiết và nhiệt độ.
Image.asset: một Widget để hiển thị một hình ảnh từ tài nguyên cục bộ
Text: một Widget để hiển thị một chuỗi được lấy từ các đối số của hàm DailyForcast.
Downloaded by Quang Tr?n (tranquang141994@gmail.com)
WeatherApp.dart
WeatherApp({Key? key}) : super(key: key);
Widget build(BuildContext context) { return ChangeNotifierProvider( create:(context) => WeatherProvider(), lazy: true, child: MaterialApp( title: "Weather App", debugShowCheckedModeBanner: false, home: MyHomePage(),
Lớp WeatherApp là một Widget không có trạng thái, lớp này trả về ChangeNotifierProvider để tạo ra một đối tượng WeatherProvider, là một lớp kế thừa từ ChangeNotifier, quản lý trạng thái của ứng dụng liên quan đến dữ liệu thời tiết
Lazy là mô ̣t boolean được chỉ định liê ̣u ChangeNotifier có được tạo ra ngay khiWeatherProvider được ch攃n vào cây Widget hay không Lazy = true là đối tượngWeatherProvider sẽ chỉ được tạo ra khi có Widget con yêu cầu nó.Sau đó nó sẽ hiển thị trang chủ của ứng dụng dự báo thời tiết.
Giao diện ứng dụng và kết luận
Giao diện ứng dụng
-Khi khơi chạy ứng dụng
Downloaded by Quang Tr?n (tranquang141994@gmail.com)
Giao di n kh i t o (Search Page) ệ ở ạ
-Sau khi nhập địa điểm là Nha Trang ta sẽ được qua giao diện chính của ứng dụng:
Downloaded by Quang Tr?n (tranquang141994@gmail.com)
Kết luận
Sau khi thực hiện dự án thiết kế ứng dụng dự báo thời tiết nhóm em đã đút kết được nhiều điều trong xuyên suốt quá trình Qua đó nhận ra được những điẻm mạnh và những điểm yếu của phần mềm:
Downloaded by Quang Tr?n (tranquang141994@gmail.com)