Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alfianyusufabdullah/f03d3d922f80e871322e1d26e304a9c8 to your computer and use it in GitHub Desktop.
Save alfianyusufabdullah/f03d3d922f80e871322e1d26e304a9c8 to your computer and use it in GitHub Desktop.
import 'package:flutter_fundamental/common/json_helper.dart';
import 'package:flutter_fundamental/data/network_data_state.dart';
import 'package:flutter_fundamental/data/network_service.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mock_web_server/mock_web_server.dart';
import 'constant.dart';
void main() {
MockWebServer _server = MockWebServer();
NetworkService _service;
setUp(() async {
await _server.start();
_service = NetworkService(_server.url);
});
tearDown(() {
_server.shutdown();
});
group("Getting All Restaurant Testing", () {
test("get all restaurant should be success", () async {
var expectedState = NetworkDataState(
data: JsonHelper().parseRestaurantList(restaurantJson));
_server.enqueue(body: restaurantJson, httpCode: 200);
var actualState = await _service.getRestaurants();
expect(actualState, isA<NetworkDataState>());
expect(actualState.isError, false);
expect(actualState.data[0].name, expectedState.data[0].name);
expect(actualState.data.length, expectedState.data.length);
});
test("get all restaurant should be success but restaurant is empty",
() async {
var expectedState = NetworkDataState(
data: JsonHelper().parseRestaurantList(emptyRestaurantJson));
_server.enqueue(body: emptyRestaurantJson, httpCode: 200);
var actualState = await _service.getRestaurants();
expect(actualState, isA<NetworkDataState>());
expect(actualState.isError, false);
expect(actualState.data.isEmpty, true);
expect(actualState.data.length, expectedState.data.length);
});
test("get all restaurant should be fail", () async {
_server.enqueue(httpCode: 404);
var actualState = await _service.getRestaurants();
expect(actualState, isA<NetworkDataState>());
expect(actualState.isError, true);
expect(actualState.data == null, true);
});
});
group("Search Restaurant By Query Testing", () {
test("search restaurant should be success", () async {
var expectedState = NetworkDataState(
data: JsonHelper().parseRestaurantList(restaurantJson));
_server.enqueue(body: restaurantJson, httpCode: 200);
var actualState = await _service.getRestaurantsByQuery("any");
expect(actualState, isA<NetworkDataState>());
expect(actualState.isError, false);
expect(actualState.data[0].name, expectedState.data[0].name);
expect(actualState.data.length, expectedState.data.length);
});
test("search restaurant should be success but restaurant is empty",
() async {
var expectedState = NetworkDataState(
data: JsonHelper().parseRestaurantList(emptyRestaurantJson));
_server.enqueue(body: emptyRestaurantJson, httpCode: 200);
var actualState = await _service.getRestaurantsByQuery("any");
expect(actualState, isA<NetworkDataState>());
expect(actualState.isError, false);
expect(actualState.data.isEmpty, true);
expect(actualState.data.length, expectedState.data.length);
});
test("search restaurant should be fail", () async {
_server.enqueue(httpCode: 404);
var actualState = await _service.getRestaurantsByQuery("any");
expect(actualState, isA<NetworkDataState>());
expect(actualState.isError, true);
expect(actualState.data == null, true);
});
});
group("Detail Restaurant Testing", () {
test("get restaurant detail should be success", () async {
var expectedState = NetworkDataState(
data: JsonHelper().parseRestaurantDetail(detailRestaurantJson));
_server.enqueue(body: detailRestaurantJson, httpCode: 200);
var actualState = await _service.getRestaurantById("any");
expect(actualState, isA<NetworkDataState>());
expect(actualState.isError, false);
expect(actualState.data.id, expectedState.data.id);
expect(actualState.data.name, expectedState.data.name);
expect(
actualState.data.description, expectedState.data.description);
expect(actualState.data.pictureId, expectedState.data.pictureId);
expect(actualState.data.city, expectedState.data.city);
expect(actualState.data.address, expectedState.data.address);
expect(actualState.data.rating, expectedState.data.rating);
});
test("get restaurant detail should be fail", () async {
_server.enqueue(httpCode: 404);
var actualState = await _service.getRestaurantById("any");
expect(actualState, isA<NetworkDataState>());
expect(actualState.isError, true);
expect(actualState.data == null, true);
});
});
group("Publishing Restaurant Review Testing", () {
test("publishing review should be success", () async {
var expectedState =
NetworkDataState(data: JsonHelper().parseReviewStatus(reviewsJson));
_server.enqueue(body: reviewsJson, httpCode: 200);
var actualState = await _service.publishReview("any review", "any");
expect(actualState, isA<NetworkDataState>());
expect(actualState.isError, false);
expect(actualState.data, expectedState.data);
});
test("publishing review should be fail", () async {
_server.enqueue(httpCode: 404);
var actualState = await _service.publishReview("any review", "any");
expect(actualState, isA<NetworkDataState>());
expect(actualState.isError, true);
expect(actualState.data == null, true);
});
});
}
import 'package:flutter_fundamental/common/json_helper.dart';
import 'package:flutter_fundamental/data/network_service.dart';
import 'package:flutter_fundamental/provider/restaurant_detail_page_provider.dart';
import 'package:flutter_fundamental/provider/restaurant_list_page_provider.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mock_web_server/mock_web_server.dart';
import 'constant.dart';
void main() {
MockWebServer _server = MockWebServer();
NetworkService _service;
setUp(() async {
await _server.start();
_service = NetworkService(_server.url);
});
tearDown(() {
_server.shutdown();
});
group("Getting All Restaurant Testing", () {
test("get all restaurant should be success", () async {
var expectedValue = JsonHelper().parseRestaurantList(restaurantJson);
RestaurantPageProvider pageProvider =
RestaurantPageProvider(networkService: _service);
_server.defaultResponse = MockResponse()
..body = restaurantJson
..httpCode = 200;
await pageProvider.getRestaurants();
expect(pageProvider.message == null, true);
expect(pageProvider.state, RestaurantListPageState.HasData);
expect(pageProvider.restaurants.length, expectedValue.length);
});
test("get all restaurant should be success but restaurant is empty",
() async {
var expectedValue = JsonHelper().parseRestaurantList(emptyRestaurantJson);
RestaurantPageProvider pageProvider =
RestaurantPageProvider(networkService: _service);
_server.defaultResponse = MockResponse()
..body = emptyRestaurantJson
..httpCode = 200;
await pageProvider.getRestaurants();
expect(pageProvider.message != null, true);
expect(
pageProvider.message ==
"Tidak dapat menampilkan informasi restaurant",
true);
expect(pageProvider.restaurants.isEmpty, true);
expect(pageProvider.restaurants, expectedValue);
expect(pageProvider.state, RestaurantListPageState.Empty);
});
test("get all restaurant should be fail", () async {
RestaurantPageProvider pageProvider =
RestaurantPageProvider(networkService: _service);
_server.defaultResponse = MockResponse()..httpCode = 404;
await pageProvider.getRestaurants();
expect(pageProvider.message != null, true);
expect(pageProvider.message, "Terjadi kesalahan saat mendapatkan data");
expect(pageProvider.state, RestaurantListPageState.Error);
});
});
group("Search Restaurant By Query Testing", () {
test("search restaurant should be success", () async {
var expectedValue = JsonHelper().parseRestaurantList(restaurantJson);
RestaurantPageProvider pageProvider =
RestaurantPageProvider(networkService: _service);
_server.defaultResponse = MockResponse()
..body = restaurantJson
..httpCode = 200;
await pageProvider.getRestaurantsByQuery("any");
expect(pageProvider.message, null);
expect(pageProvider.state, RestaurantListPageState.HasData);
expect(pageProvider.restaurants.length, expectedValue.length);
});
test("search restaurant should be success but restaurant is empty",
() async {
var expectedValue = JsonHelper().parseRestaurantList(emptyRestaurantJson);
RestaurantPageProvider pageProvider =
RestaurantPageProvider(networkService: _service);
_server.defaultResponse = MockResponse()
..body = emptyRestaurantJson
..httpCode = 200;
await pageProvider.getRestaurantsByQuery("any");
expect(pageProvider.message != null, true);
expect(
pageProvider.message ==
"Tidak dapat menampilkan informasi restaurant",
true);
expect(pageProvider.restaurants.isEmpty, true);
expect(pageProvider.restaurants, expectedValue);
expect(pageProvider.state, RestaurantListPageState.Empty);
});
test("search restaurant should be fail", () async {
RestaurantPageProvider pageProvider =
RestaurantPageProvider(networkService: _service);
_server.defaultResponse = MockResponse()..httpCode = 404;
await pageProvider.getRestaurants();
expect(pageProvider.message != null, true);
expect(pageProvider.message, "Terjadi kesalahan saat mendapatkan data");
expect(pageProvider.state, RestaurantListPageState.Error);
});
});
group("Detail Restaurant Testing", () {
test("get restaurant detail should be success", () async {
var expectedState =
JsonHelper().parseRestaurantDetail(detailRestaurantJson);
RestaurantDetailPageProvider detailPageProvider =
RestaurantDetailPageProvider(_service, "any");
_server.defaultResponse = MockResponse()
..body = detailRestaurantJson
..httpCode = 200;
await detailPageProvider.getRestaurantDetailById();
var actualValue = detailPageProvider.restaurant;
expect(detailPageProvider.state, RestaurantDetailPageState.HasData);
expect(actualValue.id, expectedState.id);
expect(actualValue.name, expectedState.name);
expect(actualValue.description, expectedState.description);
expect(actualValue.pictureId, expectedState.pictureId);
expect(actualValue.city, expectedState.city);
expect(actualValue.address, expectedState.address);
expect(actualValue.rating, expectedState.rating);
});
test("get restaurant detail should be fail", () async {
RestaurantDetailPageProvider detailPageProvider =
RestaurantDetailPageProvider(_service, "any");
_server.defaultResponse = MockResponse()..httpCode = 404;
await detailPageProvider.getRestaurantDetailById();
expect(detailPageProvider.state, RestaurantDetailPageState.Error);
expect(detailPageProvider.restaurant == null, true);
expect(detailPageProvider.message != null, true);
expect(detailPageProvider.message, "Telah terjadi error");
});
});
group("Publishing Restaurant Review Testing", () {
test("publishing review should be success", () async {
RestaurantDetailPageProvider detailPageProvider =
RestaurantDetailPageProvider(_service, "any");
_server.defaultResponse = MockResponse()
..body = reviewsJson
..httpCode = 200;
await detailPageProvider.publishReview("some review", "some id");
expect(detailPageProvider.message, "Berhasil menambahkan review baru");
});
test("publishing review should be success but return error", () async {
RestaurantDetailPageProvider detailPageProvider =
RestaurantDetailPageProvider(_service, "any");
_server.defaultResponse = MockResponse()
..body = errorReviewsJson
..httpCode = 200;
await detailPageProvider.publishReview("some review", "some id");
expect(detailPageProvider.message, "Gagal menambahkan review baru");
});
test("publishing review should be fail", () async {
RestaurantDetailPageProvider detailPageProvider =
RestaurantDetailPageProvider(_service, "any");
_server.defaultResponse = MockResponse()..httpCode = 404;
await detailPageProvider.publishReview("some review", "some id");
expect(detailPageProvider.message, "Telah terjadi error");
});
});
}
import 'package:flutter_fundamental/common/constant.dart';
import 'package:flutter_fundamental/model/menu.dart';
import 'package:flutter_fundamental/model/review.dart';
import 'package:flutter_fundamental/provider/restaurant_favorite_provider.dart';
import 'package:flutter_fundamental/ui/component/review_field.dart';
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_fundamental/data/network_service.dart';
import 'package:flutter_fundamental/model/restaurant.dart';
import 'package:flutter_fundamental/provider/restaurant_detail_page_provider.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'component/error.dart';
import 'component/loading.dart';
class RestaurantDetailPage extends StatefulWidget {
static const route = "/restaurant_detail_page";
@override
_RestaurantDetailPageState createState() => _RestaurantDetailPageState();
}
class _RestaurantDetailPageState extends State<RestaurantDetailPage>
with TickerProviderStateMixin {
bool _isPageRendered = false;
bool _isFavoriteRestaurant = false;
AnimationController _animationController;
Animation<double> _scaleAnimation;
ScrollController _scrollController;
@override
void initState() {
super.initState();
_scrollController = ScrollController();
_scrollController.addListener(() {
if (_isPageRendered) {
var value = _scrollController.position.pixels;
if (value > 300) {
_animationController.forward();
} else {
_animationController.reverse();
}
}
});
_animationController = AnimationController(
duration: Duration(milliseconds: 50), vsync: this, value: 0.1);
_scaleAnimation =
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut);
_animationController.reverse();
}
@override
void dispose() {
super.dispose();
_scrollController.dispose();
_animationController.dispose();
}
@override
Widget build(BuildContext context) {
final Restaurant restaurant = ModalRoute.of(context).settings.arguments;
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) =>
RestaurantDetailPageProvider(NetworkService(Network.BASE_URL), restaurant.id)),
ChangeNotifierProvider(
create: (_) => RestaurantFavoritePageProvider(
id: restaurant.id, isDetailPage: true),
)
],
child: Scaffold(
resizeToAvoidBottomInset: true,
floatingActionButton: ScaleTransition(
scale: _scaleAnimation,
alignment: Alignment.center,
child: Consumer<RestaurantDetailPageProvider>(
builder: (_, provider, __) {
return FloatingActionButton.extended(
onPressed: () {
showModalBottomSheet(
backgroundColor: Colors.transparent,
isScrollControlled: true,
context: context,
builder: (context) {
return ReviewFieldComponent(
onReviewSubmit: (review) {
Navigator.pop(context);
if (review.isNotEmpty)
provider.publishReview(review, restaurant.id);
},
);
},
);
},
icon: Icon(Icons.sticky_note_2_sharp),
label: Text('Tulis Review'),
);
},
),
),
body: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (context, isScrolled) {
return [
SliverAppBar(
backgroundColor: Colors.transparent,
pinned: true,
expandedHeight: 300.h,
flexibleSpace: FlexibleSpaceBar(
background: Hero(
tag: restaurant.id,
child: FadeInImage.assetNetwork(
placeholder: "assets/image/placeholder.png",
image:
'https://restaurant-api.dicoding.dev/images/small/${restaurant.pictureId}',
fit: BoxFit.cover,
),
),
),
actions: [
Consumer<RestaurantFavoritePageProvider>(
builder: (_, provider, __) {
_isFavoriteRestaurant = provider.isFavoriteRestaurant;
return GestureDetector(
onTap: () {
if(_isFavoriteRestaurant){
provider.doTransaction(
restaurant: restaurant, isAddedNewFavorite: false);
} else {
provider.doTransaction(
restaurant: restaurant, isAddedNewFavorite: true);
}
},
child: Padding(
padding: EdgeInsets.only(right: 20),
child: Icon(
_isFavoriteRestaurant ? Icons.favorite : Icons.favorite_border,
color: Colors.redAccent,
),
),
);
},
)
],
),
];
},
body: SingleChildScrollView(
physics: ScrollPhysics(),
child: Container(
padding: EdgeInsets.only(
top: 20.h, left: 20.w, right: 20.w, bottom: 100.h),
child: Consumer<RestaurantDetailPageProvider>(
builder: (_, provider, __) {
var state = provider.state;
switch (state) {
case RestaurantDetailPageState.Loading:
return LoadingComponent();
case RestaurantDetailPageState.HasData:
return _detailContent(provider);
case RestaurantDetailPageState.Error:
return ErrorComponent(
message: "Fail to load data!",
showAction: true,
onReloadClick: () {
provider.getRestaurantDetailById();
},
);
default:
}
return Center(child: Text("Looking for something?"));
},
),
),
),
),
),
);
}
Widget _detailContent(RestaurantDetailPageProvider provider) {
_isPageRendered = true;
final restaurant = provider.restaurant;
final List<Review> reviews = provider.reviews;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'${restaurant.name}',
style: TextStyle(
fontWeight: FontWeight.w300,
fontSize: 25.sp,
),
),
Padding(padding: EdgeInsets.all(4.h)),
Row(
children: [
Icon(
Icons.star,
size: 16.w,
color: Colors.orangeAccent,
),
Padding(padding: EdgeInsets.all(2.w)),
Text(
restaurant.rating.toString(),
style: TextStyle(fontSize: 14.sp),
),
],
)
],
),
Padding(padding: EdgeInsets.all(6.h)),
Text(
restaurant.description,
style: TextStyle(
fontWeight: FontWeight.w400,
color: Colors.black54,
fontSize: 14.sp,
),
),
Padding(padding: EdgeInsets.all(10.h)),
Row(
children: [
Icon(
Icons.location_on_rounded,
size: 16.w,
color: Colors.redAccent,
),
Padding(padding: EdgeInsets.all(2.w)),
Text(
_fakeLocation('${restaurant.address}${restaurant.city}'),
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.bold,
color: Colors.black45,
fontStyle: FontStyle.italic,
),
),
],
),
Padding(padding: EdgeInsets.all(16.h)),
_title("Makanan"),
_menuItems(restaurant.menus.foods, "assets/image/food.png"),
Padding(padding: EdgeInsets.all(8.h)),
_title("Minuman"),
_menuItems(restaurant.menus.drinks, "assets/image/drink.png"),
Padding(padding: EdgeInsets.all(8.h)),
Row(
children: [
_title("Review"),
Padding(padding: EdgeInsets.all(4.w)),
_newReviewCount(provider.newReviewCount),
],
),
ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: reviews.length,
itemBuilder: (_, index) {
var review = reviews[index];
return ListTile(
contentPadding: EdgeInsets.only(bottom: 8.w),
title: Padding(
padding: EdgeInsets.only(bottom: 8.0),
child: Text(review.review),
),
subtitle: Text(review.name),
leading: Image.asset("assets/image/profile.png"),
);
},
)
],
);
}
Widget _menuItems(List<MenuItem> data, String leads) => ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: data.take(4).toList().length,
itemBuilder: (_, index) {
var menu = data[index];
return ListTile(
contentPadding: EdgeInsets.only(bottom: 8.w),
title: Text(menu.name),
subtitle: Text(_fakePrice(menu.name)),
leading: Image.asset(leads),
);
},
);
Widget _title(String title) => Text(
title,
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: 18.sp,
),
);
Widget _newReviewCount(int count) {
if (count > 0) {
return Container(
decoration: BoxDecoration(
color: Colors.redAccent,
borderRadius: BorderRadius.all(
Radius.circular(20),
)),
child: Padding(
padding: EdgeInsets.all(6.0),
child: Text(
'+$count',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.normal,
),
),
),
);
} else {
return Text('');
}
}
String _fakePrice(String name) {
return 'Rp. ${name.length},000';
}
String _fakeLocation(String address) {
return '${address.length / 100} KM dari lokasi kamu sekarang';
}
}
RestaurantFavoritePageProvider({String id, bool isDetailPage}) {
_databaseHelper = DatabaseHelper();
loadFavoriteRestaurant();
if (isDetailPage) {
checkIfRestaurantIsFavorite(id);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment