Skip to content

Commit

Permalink
Merge pull request #74 from nimblehq/release/0.5.0
Browse files Browse the repository at this point in the history
[Release] 0.5.0
  • Loading branch information
nkhanh44 authored Aug 31, 2023
2 parents 7e01764 + e1678b1 commit 1eff657
Show file tree
Hide file tree
Showing 18 changed files with 466 additions and 47 deletions.
Binary file added assets/images/dummy_background.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions assets/svg/close_button.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@
"genericError": "Something went wrong.\nPlease try again!",
"loginFailAlertTitle": "Unable to log in",
"today": "Today",
"errorText": "Error"
"errorText": "Error",
"startSurveyText": "Start Survey",
"submitText": "Submit"
}
22 changes: 17 additions & 5 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ import 'package:flutter_config/flutter_config.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:survey_flutter/model/survey_model.dart';
import 'package:survey_flutter/screens/app_route.dart';
import 'package:survey_flutter/screens/home/home_screen.dart';
import 'package:survey_flutter/screens/login/login_screen.dart';
import 'package:survey_flutter/screens/splash/splash_screen.dart';
import 'package:survey_flutter/screens/survey/survey_detail_screen.dart';
import 'package:survey_flutter/screens/survey/survey_screen.dart';
import 'package:survey_flutter/storage/survey_storage.dart';
import 'package:survey_flutter/theme/app_theme.dart';

Expand All @@ -20,27 +24,35 @@ void main() async {
);
}

const routePathSplashScreen = '/';

class App extends StatelessWidget {
App({Key? key}) : super(key: key);

final GoRouter _router = GoRouter(
routes: <GoRoute>[
GoRoute(
path: routePathSplashScreen,
path: AppRoute.splash.path,
builder: (_, __) => const SplashScreen(),
),
GoRoute(
path: routePathLoginScreen,
path: AppRoute.login.path,
pageBuilder: (_, __) => const NoTransitionPage<void>(
child: LoginScreen(),
),
),
GoRoute(
path: routePathHomeScreen,
path: AppRoute.home.path,
builder: (_, __) => const HomeScreen(),
),
GoRoute(
path: AppRoute.survey.path,
builder: (_, state) => SurveyDetailScreen(
survey: state.extra as SurveyModel,
),
),
GoRoute(
path: AppRoute.questions.path,
builder: (_, __) => SurveyScreen(),
),
],
);

Expand Down
24 changes: 24 additions & 0 deletions lib/screens/app_route.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
enum AppRoute {
splash,
login,
home,
survey,
questions,
}

extension AppRoutePath on AppRoute {
String get path {
switch (this) {
case AppRoute.splash:
return '/';
case AppRoute.login:
return '/login';
case AppRoute.home:
return '/home';
case AppRoute.survey:
return '/survey';
case AppRoute.questions:
return '/questions';
}
}
}
53 changes: 38 additions & 15 deletions lib/screens/home/home_pages_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,51 @@ import '../../gen/assets.gen.dart';

const _imageOpacity = 0.6;

class HomePagesWidget extends StatelessWidget {
class HomePagesWidget extends StatefulWidget {
final List<SurveyModel> surveys;
final ValueNotifier<int> currentPage;
final PageController _pageController = PageController();
final VoidCallback onNextButtonPressed;
final bool isRefreshing;
final VoidCallback onLoadMore;

HomePagesWidget({
const HomePagesWidget({
Key? key,
required this.surveys,
required this.currentPage,
required this.onNextButtonPressed,
required this.onLoadMore,
required this.isRefreshing,
}) : super(key: key);

@override
State<HomePagesWidget> createState() => _HomePagesWidgetState();
}

class _HomePagesWidgetState extends State<HomePagesWidget> {
final PageController _pageController = PageController(initialPage: 0);

@override
void dispose() {
_pageController.dispose();
super.dispose();
}

void _handlePageChanged(int index) {
widget.currentPage.value = index;

if (index == widget.surveys.length - 1) {
widget.onLoadMore();
}
}

@override
Widget build(BuildContext context) {
if (widget.isRefreshing && widget.surveys.isNotEmpty) {
_pageController.jumpToPage(0);
}

return PageView.builder(
itemCount: surveys.length,
itemCount: widget.surveys.length,
controller: _pageController,
itemBuilder: (_, int index) {
return Container(
Expand All @@ -32,7 +62,7 @@ class HomePagesWidget extends StatelessWidget {
opacity: _imageOpacity,
child: FadeInImage.assetNetwork(
placeholder: Assets.images.placeholder.path,
image: surveys[index].coverImageUrl,
image: widget.surveys[index].coverImageUrl,
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
Expand All @@ -49,13 +79,8 @@ class HomePagesWidget extends StatelessWidget {
right: 0,
),
child: HomeFooterWidget(
survey: surveys[index],
onNextButtonPressed: () {
if (currentPage.value < surveys.length - 1) {
currentPage.value = index + 1;
_pageController.jumpToPage(currentPage.value);
}
},
survey: widget.surveys[index],
onNextButtonPressed: widget.onNextButtonPressed,
),
),
),
Expand All @@ -65,9 +90,7 @@ class HomePagesWidget extends StatelessWidget {
),
);
},
onPageChanged: (int index) {
currentPage.value = index;
},
onPageChanged: _handlePageChanged,
);
}
}
33 changes: 26 additions & 7 deletions lib/screens/home/home_screen.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:survey_flutter/model/survey_model.dart';
import 'package:survey_flutter/screens/app_route.dart';
import 'package:survey_flutter/screens/home/home_header_widget.dart';
import 'package:survey_flutter/screens/home/home_pages_widget.dart';
import 'package:survey_flutter/screens/home/home_page_indicator_widget.dart';
import 'package:survey_flutter/screens/home/home_pages_widget.dart';
import 'package:survey_flutter/screens/home/home_shimmer_loading.dart';
import 'package:survey_flutter/screens/home/home_view_model.dart';
import 'package:survey_flutter/utils/build_context_ext.dart';
import 'package:survey_flutter/widgets/alert_dialog.dart';

const routePathHomeScreen = '/home';

class HomeScreen extends ConsumerStatefulWidget {
const HomeScreen({Key? key}) : super(key: key);

Expand All @@ -33,8 +33,14 @@ class _HomeScreenState extends ConsumerState<HomeScreen> {
_initData();
}

Future<void> _initData() async {
ref.read(homeViewModelProvider.notifier).loadSurveys(isRefreshing: false);
void _initData() {
_loadSurveys();
}

Future<void> _loadSurveys({bool isRefreshing = false}) async {
ref
.read(homeViewModelProvider.notifier)
.loadSurveys(isRefreshing: isRefreshing);
}

@override
Expand All @@ -43,11 +49,15 @@ class _HomeScreenState extends ConsumerState<HomeScreen> {
loading: () => _buildHomeScreen(isLoading: true),
error: () => _buildHomeScreen(),
loadCachedSurveysSuccess: () => _buildHomeScreen(),
loadSurveysSuccess: () => _buildHomeScreen(),
loadSurveysSuccess: (isRefreshing) =>
_buildHomeScreen(isRefreshing: isRefreshing),
);
}

Widget _buildHomeScreen({bool isLoading = false}) {
Widget _buildHomeScreen({
bool isLoading = false,
bool isRefreshing = false,
}) {
final surveys = ref.watch(_surveysStreamProvider).value ?? [];
final errorMessage = ref.watch(_errorStreamProvider).value ?? "";

Expand Down Expand Up @@ -85,6 +95,15 @@ class _HomeScreenState extends ConsumerState<HomeScreen> {
HomePagesWidget(
surveys: surveys,
currentPage: _currentPage,
onNextButtonPressed: () {
final survey = surveys[_currentPage.value];
context.push(
AppRoute.survey.path,
extra: survey,
);
},
onLoadMore: _loadSurveys,
isRefreshing: isRefreshing,
),
const HomeHeaderWidget(),
Align(
Expand Down
3 changes: 2 additions & 1 deletion lib/screens/home/home_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ class HomeState with _$HomeState {
const factory HomeState.loadCachedSurveysSuccess() =
_LoadCachedSurveysSuccess;

const factory HomeState.loadSurveysSuccess() = _LoadSurveysSuccess;
const factory HomeState.loadSurveysSuccess(bool isRefreshing) =
_LoadSurveysSuccess;

const factory HomeState.error() = _error;
}
34 changes: 26 additions & 8 deletions lib/screens/home/home_view_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import 'package:survey_flutter/usecases/base/base_use_case.dart';
import 'package:survey_flutter/usecases/get_cached_surveys_use_case.dart';
import 'package:survey_flutter/usecases/get_surveys_use_case.dart';

// TODO: Integrate load more
const _pageNumber = 1;
int _pageNumber = 1;
const _pageSize = 10;
List<SurveyModel> _loadedSurveys = [];

final homeViewModelProvider =
StateNotifierProvider.autoDispose<HomeViewModel, HomeState>((ref) {
Expand Down Expand Up @@ -39,25 +39,43 @@ class HomeViewModel extends StateNotifier<HomeState> {
final _error = StreamController<String>();
Stream<String> get error => _error.stream;

void _handleError(Failed result) {
if (result.isNotFoundError()) {
_surveys.add(_loadedSurveys);
state = const HomeState.loadSurveysSuccess(false);
} else {
_error.add(result.getErrorMessage());
state = const HomeState.error();
}
}

Future<void> loadSurveys({required bool isRefreshing}) async {
if (!isRefreshing) {
_loadSurveysFromCache();
}
_loadSurveysFromRemote();
_loadSurveysFromRemote(isRefreshing: isRefreshing);
}

void _loadSurveysFromRemote() async {
void _loadSurveysFromRemote({required bool isRefreshing}) async {
if (isRefreshing) {
_loadedSurveys.clear();
_pageNumber = 1;
}
final result = await _getSurveysUseCase.call(SurveysParams(
pageNumber: _pageNumber,
pageSize: _pageSize,
));

if (result is Success<List<SurveyModel>>) {
final newSurveys = result.value;
_surveys.add(newSurveys);
state = const HomeState.loadSurveysSuccess();
if (newSurveys.isNotEmpty) {
_loadedSurveys.addAll(newSurveys);
_surveys.add(_loadedSurveys);
state = HomeState.loadSurveysSuccess(isRefreshing);
_pageNumber++;
}
} else if (result is Failed) {
_error.add((result as Failed).getErrorMessage());
state = const HomeState.error();
_handleError(result as Failed);
}
}

Expand Down
6 changes: 2 additions & 4 deletions lib/screens/login/login_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:survey_flutter/gen/assets.gen.dart';
import 'package:survey_flutter/screens/home/home_screen.dart';
import 'package:survey_flutter/screens/app_route.dart';
import 'package:survey_flutter/screens/login/login_form.dart';
import 'package:survey_flutter/screens/login/login_view_model.dart';
import 'package:survey_flutter/theme/app_constants.dart';
import 'package:survey_flutter/uimodels/app_error.dart';
import 'package:survey_flutter/utils/build_context_ext.dart';
import 'package:survey_flutter/widgets/alert_dialog.dart';

const routePathLoginScreen = '/login';

class LoginScreen extends ConsumerStatefulWidget {
const LoginScreen({Key? key}) : super(key: key);

Expand Down Expand Up @@ -117,7 +115,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
_setUpListener(BuildContext context) {
ref.listen<AsyncValue<void>>(loginViewModelProvider, (_, next) {
next.maybeWhen(
data: (_) => context.go(routePathHomeScreen),
data: (_) => context.go(AppRoute.home.path),
error: (error, _) {
showAlertDialog(
context: context,
Expand Down
7 changes: 3 additions & 4 deletions lib/screens/splash/splash_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:survey_flutter/gen/assets.gen.dart';
import 'package:survey_flutter/screens/home/home_screen.dart';
import 'package:survey_flutter/screens/login/login_screen.dart';
import 'package:survey_flutter/screens/app_route.dart';
import 'package:survey_flutter/screens/splash/splash_view_model.dart';

class SplashScreen extends ConsumerStatefulWidget {
Expand Down Expand Up @@ -57,9 +56,9 @@ class _SplashScreenState extends ConsumerState<SplashScreen> {
child: Assets.images.splashLogoWhite.image(),
onEnd: () {
if (_isLoggedIn == true) {
context.go(routePathHomeScreen);
context.go(AppRoute.home.path);
} else {
context.go(routePathLoginScreen);
context.go(AppRoute.login.path);
}
},
);
Expand Down
Loading

0 comments on commit 1eff657

Please sign in to comment.