sharedPreference能將簡單的資料存放在硬碟內,採用key-value的方式做存取。
在Flutter要使用sharedPreference非常簡單,首先先下載「shared_preferences」套件。
在pubspec.yaml dependencies處添加 shared_preferences: ^0.5.3+4
安裝最新版本。
要增加的程式碼並不多。
import 'package:shared_preferences/shared_preferences.dart';
bool _rememberAccount;
_loadCheckbox() async{
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_rememberAccount = prefs.getBool('remember_account') ?? false;
});
}
_saveCheckout(value) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setBool('remember_account', value);
}
_loadAccount() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_emailController.text = prefs.getString('account') ?? "";
});
}
_saveAccount(account) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('account', account);
}
void initState() {
_loginBloc = BlocProvider.of<LoginBloc>(context);
_emailController.addListener(_onEmailChanged);
_passwordController.addListener(_onPasswordChanged);
_loadCheckbox();
_loadAccount();
super.initState();
}
CheckboxListTile(
value: _rememberAccount ?? false,
onChanged: (value){
setState(() {
_rememberAccount = value;
});
_saveCheckout(value);
},
title: Text('記住我的帳號'),
controlAffinity: ListTileControlAffinity.leading,
)
if (state.isSuccess) {
if(_rememberAccount)
_saveAccount(_emailController.text);
BlocProvider.of<AuthenticationBloc>(context).dispatch(LoggedIn());
}
處理完記錄帳號的功能,回到今天的正題來串接TMDb API吧,TMDb的API是免費的除了提供電影資訊外它也提供名人和TV Show的資料可以使用。
在瀏覽器網址輸入https://api.themoviedb.org/3/movie/now_playing?api_key=[Your API KEY]&page=1®ion=TW
,把你的API Key替換進去,應該會看到如下圖的畫面,這些就是TMDb回傳的資料,我們在App裡面就要轉換它們並存成Movie的物件。
如果你覺得這樣看太亂不舒服,可以使用Firefox測試,Firefox偵測到是json格式就會自動的整理排版。
可以到TMDb的API Document查看如何使用他們的API,也能直接在網頁上進行測試,測試好直接複製它們產生的網址之後就可以使用網址取得資料。
這次會使用到以下幾種電影清單類型:
可以先在網頁上玩看看~
新增「movie」資料夾在裡面新增「model」資料夾,最後新增「movie_list.dart」。
路徑:lib/movie/model/movie_list.dart
這裡我們要自己建立MovieList類別,還沒有class概念的人可以參考Day5的教學。
class MovieList {
int _page;
int _totalResults;
int _totalPages;
List<_Item> _results = [];
MovieList.fromJson(Map<String, dynamic> parsedJson) {
_page = parsedJson['page'];
_totalPages = parsedJson['total_pages'];
_totalResults = parsedJson['total_results'];
List<_Item> temp = [];
for (int i=0; i < parsedJson['results'].length; i++){
_Item item = _Item(parsedJson['results'][i]);
temp.add(item);
}
_results = temp;
}
List<_Item> get movieList => _results;
int get totalPages => _totalPages;
int get totalResults => _totalResults;
int get page => _page;
}
class _Item {
var _popularity;
int _voteCount;
int _id;
bool _video;
var _voteAverage;
String _title;
String _posterPath;
String _backdropPath;
String _language;
String _overview;
String _releaseDate;
_Item(result) {
_popularity = result['popularity'];
_voteCount = result['vote_count'];
_id = result['id'];
_video = result['video'];
_voteAverage = result['vote_average'] + 0.0;
_title = result['title'];
_posterPath = result['poster_path'];
_backdropPath = result['backdrop_path'];
_language = result['original_language'];
_overview = result['overview'];
_releaseDate = result['release_date'];
}
String get popularity => _popularity;
int get voteCount => _voteCount;
int get id => _id;
bool get video => _video;
double get voteAverage => _voteAverage;
String get title => _title;
String get posterPath => _posterPath;
String get backdropPath => _backdropPath;
String get language => _language;
String get overview => _overview;
String get releaseDate => _releaseDate;
}
雖然程式碼很長,不過概念很簡單,在這個檔案中我們創造兩個class:
還記得Day5提過的name constructor嗎?MovieList.fromJson(Map<String, dynamic> parsedJson)
就是一個name constructor,它能接收從Json data decode來的Map object用來初始化MovieList Object。
在movie資料夾下新增movie_api_provider.dart
由於在這個檔案中我們要使用到http來呼叫IMDb API,所以先到pubspec.yaml增加「http」套件的引用
http: ^0.12.0+2
下載好後來看程式碼吧。
import 'package:http/http.dart';
import 'dart:async';
import 'dart:convert';
import 'model/movie_list.dart';
import 'movie_api_key.dart';
class MovieApiProvider {
final Client _client;
String _apiKey = movie_api_key;
final _baseUrl = 'http://api.themoviedb.org/3/movie';
MovieApiProvider({Client client})
: _client = client ?? Client();
Future<MovieList> fetchPopularMovieList({String region = 'TW'}) async {
final response = await _client
.get("$_baseUrl/popular?api_key=$_apiKey&page=1®ion=$region");
if (response.statusCode == 200) {
return MovieList.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to get popular movie list.');
}
}
Future<MovieList> fetchNowPlayingMovieList({String region = 'TW'}) async {
final response = await _client
.get("$_baseUrl/now_playing?api_key=$_apiKey&page=1®ion=$region");
if (response.statusCode == 200) {
return MovieList.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to get now playing movie list.');
}
}
Future<MovieList> fetchTopRatedMovieList({String region = 'TW'}) async {
final response = await _client
.get("$_baseUrl/top_rated?api_key=$_apiKey&page=1®ion=$region");
if (response.statusCode == 200) {
return MovieList.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to get top rated movie list.');
}
}
}
你的api_key可以像我一樣放在另一個檔案「movie_api_key.dart」,裡面只有一行程式碼const String movie_api_key = "your_api_key";
建構子需要client參數是為了之後測試方便才加的,目前先不用理會它。
這3個method使用http的Get傳送Request和Server索取前面提到的「Now Playing」、「Popular」和「Top Rated」電影清單。接收到Response後確認statusCode,假如是200代表成功取得資料。
其他statusCode對應的訊息可以在這裡查詢。
在movie資料夾下新增movie_repository.dart
import 'dart:async';
import 'movie_api_provider.dart';
import 'model/movie_list.dart';
class MovieRepository {
final _movieApiProvider = MovieApiProvider();
Future<MovieList> fetchPopularMovieList({String region = 'TW'}) =>
_movieApiProvider.fetchPopularMovieList(region: region);
Future<MovieList> fetchNowPlayingMovieList({String region = 'TW'}) =>
_movieApiProvider.fetchNowPlayingMovieList(region: region);
Future<MovieList> fetchTopRatedMovieList({String region = 'TW'}) =>
_movieApiProvider.fetchTopRatedMovieList(region: region);
}
其實只是多包裝一層Repository class,這是因為使用MovieRepository的人不需要知道我們是如何呼叫TMDb API的。
之後實作的MovieBloc如果要使用到Movie API就直接呼叫MovieRepository而不是MovieApiProvider。
fluttube
└───lib
│ └───movie
│ │ └───model
│ │ │ └───movie_list.dart
│ │ └───movie_api_provider.dart
│ │ └───movie_repository.dart
│ └───main.dart
│ ...
│ └─── validators.dart
今天前半部幫登入介面新增了記錄帳號的功能,也順便學習如何使用SharedPreference套件(雖然也可以使用SharedPreference來記錄使用者的密碼,不過礙於安全性就不推薦了)。
後半部認識了TMDb API該如何設定和使用。根據回傳的Json Data建立了MovieList Class。
並在App中新增了MovieApiProvider和MovieRepository來串接API,之後就能直接使用MovieRepository內的method取得MovieList。
明天老樣子來實作MovieBloc吧,這已經是實作的第四個Bloc應該很熟悉了迅速完成它吧。
完整程式碼在這裡-> FlutTube Github
您好:
我覺得您的專案看起很棒,想研究一下,
請問目前一個地方在 movie_api_key.dart,
專案說找不到這個dart,請問這來源是什麼呢? 好像也不是外來的
文章中有提到呦
你的api_key可以像我一樣放在另一個檔案「movie_api_key.dart」,裡面只有一行程式碼const String movie_api_key = "your_api_key";
可以嘍!! 感謝!