這是我第一次寫(不專業技術文,請大家多多包涵),希望大家會喜歡。可以留言問我問題,或是給我一點寫技術文件的意見。我有看到都會回復呦~
|-- lib
|-- bloc // bloc 資料夾
|-- restaurant_bloc.bloc.dart
|-- restaurant_bloc.event.dart
|-- restaurant_bloc.state.dart
|-- data // 用來裝 respository、model
|-- model
|-- api_result_model.dart
|-- respository
|-- restaurant_respository.dart
|-- res
|-- string
|-- strings.dart
|-- ui
|-- pages
|-- about_page.dart
|-- home_page.dart
|-- main.dart
{
"success" : true,
"message" : "SUCCESS",
"data" : [ {
"id" : 0,
"name" : "八方雲集沙鹿靜宜店",
"description" : "營業時間:10:00 – 21:00\n中午不外送",
"phone" : "0426321128",
"address" : "台中市沙鹿區英才路17號",
"lowest_price" : 300
}, {
"id" : 1,
"name" : "偉哥鹹酥雞台中靜宜店",
"description" : "營業時間:15:30 – 12:15\n沒有提供外送服務",
"phone" : "0423809838",
"address" : "臺中市沙鹿區北勢東路517之1號",
"lowest_price" : 0
} ]
}
我是使用 http 的套件來串接 API。
官網介紹
另外一種也是很多人用的 API 串接方式。(當然還有很多,在這邊不一一介紹)。
Dio
我是使用 quicktype 來幫我自動轉成我要的物件。
Bloc 分成 3 部分,Event Bloc State
就是當你要取得資料====> 事件。
當你與畫面進行互動====> 事件。
取得資料中 => 狀態
取資料成功 => 狀態
取資料失敗 => 狀態
去做判斷
如果這個事件 = 取得資料 那就屬於什麼狀態
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Api-Get',
home: BlocProvider(
create: (context) => RestaurantBloc(restaurantRepository: RestaurantRepository()),
child: HomePage(),
),
);
}
}
abstract class RestaurantEvent extends Equatable{
@override
List<Object> get props =>[];
}
class FetchRestaurantEvent extends RestaurantEvent {}
abstract class RestaurantState extends Equatable {
@override
List<Object> get props => [];
}
class RestaurantLoadingState extends RestaurantState {
}
class RestaurantSuccessState extends RestaurantState {
final RestaurantModel restaurantModel;
RestaurantSuccessState({@required this.restaurantModel});
@override
// TODO: implement props
List<Object> get props => [restaurantModel];
}
class RestaurantFailState extends RestaurantState {
final String message;
RestaurantFailState({@required this.message});
@override
// TODO: implement props
List<Object> get props => [message];
}
class RestaurantBloc extends Bloc<RestaurantEvent, RestaurantState> {
RestaurantRepository restaurantRepository;
RestaurantBloc({@required this.restaurantRepository}) : super(RestaurantLoadingState());
RestaurantState get initialState => RestaurantLoadingState();
@override
Stream<RestaurantState> mapEventToState(RestaurantEvent event) async* {
if (event is FetchRestaurantEvent) {
yield RestaurantLoadingState();
try {
RestaurantModel restaurantModel = await restaurantRepository.getRestaurantData();
print("Bloc Success");
yield RestaurantSuccessState(restaurantModel: restaurantModel);
} catch (e) {
print( await restaurantRepository.getRestaurantData());
yield RestaurantFailState(message: "???");
}
}
}
}
class RestaurantRepository {
Future<RestaurantModel> getRestaurantData() async {
final response = await http.get(AppStrings.cricArticleUrl);
if (response.statusCode == 200) {
List<int> bytes = response.body.toString().codeUnits;
var responseString = utf8.decode(bytes);
return RestaurantModel.fromJson(jsonDecode(responseString));
} else {
print("EEEE");
throw Exception();
}
}
}
這部分請參考 quicktype 轉成物件。
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
RestaurantBloc _restaurantBloc;
Size size;
@override
void initState() {
super.initState();
_restaurantBloc = BlocProvider.of<RestaurantBloc>(context);
_restaurantBloc.add(FetchRestaurantEvent());
}
@override
Widget build(BuildContext context) {
size = MediaQuery.of(context).size;
return MaterialApp(
home: Builder(
builder: (context) {
return Material(
child: Scaffold(
appBar: AppBar(
title: Text("Api-Get"),
centerTitle: true,
actions: <Widget>[
IconButton(
icon: Icon(Icons.refresh),
onPressed: () {
_restaurantBloc.add(FetchRestaurantEvent());
},
),
IconButton(
icon: Icon(Icons.info),
onPressed: () {
navigateToAboutPage(context);
},
)
],
),
body: Container(
child: BlocListener<RestaurantBloc, RestaurantState>(
listener: (context, state) {
if (state is RestaurantFailState) {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(state.message),
),
);
}
},
child: BlocBuilder<RestaurantBloc, RestaurantState>(
builder: (context, state) {
if (state is RestaurantLoadingState) {
return _buildLoading();
} else if (state is RestaurantSuccessState) {
return buildArticleList(state.restaurantModel);
} else if (state is RestaurantFailState) {
return _buildErrorUi(state.message);
}
return Container();
},
),
),
),
),
);
},
),
);
}
Widget _buildLoading() {
return Center(
child: CircularProgressIndicator(),
);
}
Widget _buildErrorUi(String message) {
return Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
message,
style: TextStyle(color: Colors.red),
),
),
);
}
Widget buildArticleList(RestaurantModel restaurantModel) {
return Container(
child: new ListView.builder(
padding: EdgeInsets.only(top: 30),
itemCount: restaurantModel.data.length,
itemBuilder: (context, int index) =>
buildCustomItem(context, index, restaurantModel.data),
));
}
void navigateToAboutPage(BuildContext context) {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return AboutPage();
}));
}
/// 餐廳 listView 的 Item 畫面
Widget buildCustomItem(BuildContext context, int index, List<Datum> data) {
return Container(
padding:
EdgeInsets.only(left: size.width * 0.1, right: size.width * 0.1),
// height: 500,
width: size.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Container(
// height: size.height * 0.3,
// decoration: BoxDecoration(
// borderRadius: BorderRadius.all(Radius.circular(25.0))),
// child: Image.network(RestaurantList[index].storeLink),
// // child: _ImgaeNetWorkStyle(RestaurantList[index].storeLink)
// ),
Padding(
padding: EdgeInsets.only(top: 16, right: 8.0, left: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
data[index].name,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.cyan[700]),
),
Text(
"最低\$" + data[index].lowestPrice.toString() + "外送",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.cyan[700]),
),
],
),
),
Padding(
padding: EdgeInsets.all(8),
child: Text(data[index].description,
style: TextStyle(fontSize: 15, color: Colors.cyan[700])),
),
Padding(
padding: EdgeInsets.only(bottom: 16, right: 8.0, left: 8.0),
child: Text(data[index].address,
style: TextStyle(fontSize: 15, color: Colors.cyan[700])),
),
],
));
}
}
class AboutPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("About"),
),
body: Container(
alignment: Alignment.center,
child: Text("Developer : wayne900204@gmail.com"),
),
);
}
}
這是以上我寫 Flutter 並使用 Bloc 來串接 API 的實際例子。
這是我的 Github 開源碼 Flutter-Api-Get-Bloc 可以參考這裡。