From 6d0df77cc333f5e3a4a8a554d10837c1891c4978 Mon Sep 17 00:00:00 2001 From: ardiprima_ Date: Sun, 28 Feb 2021 11:36:48 +0700 Subject: [PATCH] save session level tod + user tod load more --- .../authentication/auth_bloc.dart | 2 + .../truth_or_dare/user_tod/user_tod_bloc.dart | 46 ++- .../user_tod/user_tod_event.dart | 16 +- lib/config/router.dart | 5 +- lib/constants/route_paths.dart | 3 +- lib/screen/truth_or_dare/home.dart | 349 +++++++++++------- lib/screen/truth_or_dare/user_dare.dart | 95 +++-- lib/screen/truth_or_dare/user_tod.dart | 4 + lib/screen/truth_or_dare/user_truth.dart | 33 +- 9 files changed, 376 insertions(+), 177 deletions(-) diff --git a/lib/blocs/truth_or_dare/authentication/auth_bloc.dart b/lib/blocs/truth_or_dare/authentication/auth_bloc.dart index 57a5e1f..e01f5f4 100644 --- a/lib/blocs/truth_or_dare/authentication/auth_bloc.dart +++ b/lib/blocs/truth_or_dare/authentication/auth_bloc.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; +import 'package:truthordare/utilities/SharedPreferences.dart'; import 'package:truthordare/utilities/local_storage_helper.dart'; import 'package:truthordare/utilities/secure_store.dart'; @@ -22,6 +23,7 @@ class AuthBloc extends Bloc { yield AuthAuthenticated(token: event.token); }else if(event is UserLoggedOut){ await SecureStore().deleteAll(); + await Preferences.getDataBool("level"); await LocalStorageHelper.clearStorage(storageName: StorageName.USER); yield AuthUnauthenticated(); } diff --git a/lib/blocs/truth_or_dare/user_tod/user_tod_bloc.dart b/lib/blocs/truth_or_dare/user_tod/user_tod_bloc.dart index b7973f0..9f2fca8 100644 --- a/lib/blocs/truth_or_dare/user_tod/user_tod_bloc.dart +++ b/lib/blocs/truth_or_dare/user_tod/user_tod_bloc.dart @@ -12,23 +12,30 @@ part 'user_tod_state.dart'; class UserTodBloc extends Bloc { final TruthOrDareRepository truthOrDareRepository; UserTodBloc({this.truthOrDareRepository}) : super(UserTodInitial()); - + List userDare; + List userTruth; @override Stream mapEventToState( UserTodEvent event, ) async* { if(event is GetUserDare){ + userDare=[]; yield* _getUserDareToState(event); }else if(event is GetUserTruth){ + userTruth=[]; yield* _getUserTruthToState(event); }else if(event is DeleteUserTod){ yield* _deleteUserTodToState(event); + }else if(event is GetMoreUserTruth){ + yield* _getMoreUserTruthToState(event); + }else if(event is GetMoreUserDare){ + yield* _getMoreUserDareToState(event); } } Stream _getUserTruthToState(GetUserTruth event)async* { yield UserTodLoading(); try{ - List userTruth=[]; + int currentPage=1; bool hasReachedMax=false; UserTruthModel userTruthModel=await truthOrDareRepository.getUserTod(type: "truth",page:currentPage); @@ -46,7 +53,6 @@ class UserTodBloc extends Bloc { Stream _getUserDareToState(GetUserDare event)async* { yield UserTodLoading(); try{ - List userDare=[]; int currentPage=1; bool hasReachedMax=false; UserDareModel userDareModel=await truthOrDareRepository.getUserTod(type: "dare",page:currentPage); @@ -77,4 +83,38 @@ class UserTodBloc extends Bloc { yield UserTodFailure(message: e.toString()??"an error occured"); } } + + Stream _getMoreUserTruthToState(GetMoreUserTruth event)async* { + try{ + int currentPage=event.currentPage; + bool hasReachedMax=false; + UserTruthModel userTruthModel=await truthOrDareRepository.getUserTod(type: "truth",page:currentPage+=1); + if(userTruthModel.results.lastPage==currentPage){ + hasReachedMax=true; + } + userTruthModel.results.data.forEach((element) { + userTruth.add(element); + }); + yield UserTruthLoaded(userTruth: userTruth,hasReachedMax: hasReachedMax,currentPage: currentPage); + }catch(e){ + yield UserTodFailure(message: e.toString()??"an error occured"); + } + } + + Stream _getMoreUserDareToState(GetMoreUserDare event)async* { + try{ + int currentPage=event.currentPage; + bool hasReachedMax=false; + UserDareModel userDareModel=await truthOrDareRepository.getUserTod(type: "dare",page:currentPage+=1); + if(userDareModel.results.lastPage==currentPage){ + hasReachedMax=true; + } + userDareModel.results.data.forEach((element) { + userDare.add(element); + }); + yield UserDareloaded(userDare: userDare,hasReachedMax: hasReachedMax,currentPage: currentPage); + }catch(e){ + yield UserTodFailure(message: e.toString()??"an error occured"); + } + } } diff --git a/lib/blocs/truth_or_dare/user_tod/user_tod_event.dart b/lib/blocs/truth_or_dare/user_tod/user_tod_event.dart index 2274e44..9cc4362 100644 --- a/lib/blocs/truth_or_dare/user_tod/user_tod_event.dart +++ b/lib/blocs/truth_or_dare/user_tod/user_tod_event.dart @@ -6,7 +6,14 @@ abstract class UserTodEvent extends Equatable { List get props => []; } class GetUserTruth extends UserTodEvent{} -class GetMoreUserTruth extends UserTodEvent{} +class GetMoreUserTruth extends UserTodEvent{ + final int currentPage; + + GetMoreUserTruth({this.currentPage}); + @override + List get props => [currentPage]; + +} class DeleteUserTod extends UserTodEvent{ final String type; final String uuid; @@ -16,4 +23,9 @@ class DeleteUserTod extends UserTodEvent{ List get props => [type,uuid]; } class GetUserDare extends UserTodEvent{} -class GetMoreUserDare extends UserTodEvent{} +class GetMoreUserDare extends UserTodEvent{ + final int currentPage; + GetMoreUserDare({this.currentPage}); + @override + List get props => [currentPage]; +} diff --git a/lib/config/router.dart b/lib/config/router.dart index 667e12e..d56cffe 100644 --- a/lib/config/router.dart +++ b/lib/config/router.dart @@ -6,15 +6,18 @@ import 'package:truthordare/screen/dashboard.dart'; import 'package:truthordare/screen/login_screen/login.dart'; import 'package:truthordare/screen/login_screen/register.dart'; +import 'package:truthordare/screen/truth_or_dare/submit_tod.dart'; import 'package:truthordare/screen/truth_or_dare/user_tod.dart'; Route generateRoute(RouteSettings settings) { switch (settings.name) { case routes.homeRoute: - return MaterialPageRoute(builder: (_)=>Dashboard()); + return MaterialPageRoute(builder: (_)=>Dashboard(initialPage: settings.arguments??0,)); case routes.loginRoute: return MaterialPageRoute(builder: (_)=>Login()); + case routes.submitTodRoute: + return MaterialPageRoute(builder: (_)=>SubmitTod()); case routes.registerRoute: return MaterialPageRoute(builder: (_)=>Register()); case routes.userTodRoute: diff --git a/lib/constants/route_paths.dart b/lib/constants/route_paths.dart index f9764b3..a53456c 100644 --- a/lib/constants/route_paths.dart +++ b/lib/constants/route_paths.dart @@ -2,4 +2,5 @@ const String homeRoute='/home'; const String loginRoute='/login'; const String userTodRoute='/user_tod'; const String registerRoute='/register'; -const String viewPhotoRoute='/view_photo'; \ No newline at end of file +const String viewPhotoRoute='/view_photo'; +const String submitTodRoute='/submit_tod'; \ No newline at end of file diff --git a/lib/screen/truth_or_dare/home.dart b/lib/screen/truth_or_dare/home.dart index ed8df4a..6b11f5d 100644 --- a/lib/screen/truth_or_dare/home.dart +++ b/lib/screen/truth_or_dare/home.dart @@ -5,10 +5,28 @@ import 'package:truthordare/constants/Lists.dart'; import 'package:truthordare/service_locator.dart'; import 'package:truthordare/constants/Colors.dart'; import 'package:truthordare/constants/Dictionary.dart'; +import 'package:truthordare/utilities/SharedPreferences.dart'; import 'package:truthordare/utilities/string_helper.dart'; import 'package:shimmer/shimmer.dart'; -class Home extends StatelessWidget { +class Home extends StatefulWidget { + @override + _HomeState createState() => _HomeState(); +} + +class _HomeState extends State { + int selectedLevel; + + Future setSelectedLevel() async { + selectedLevel = await Preferences.getDataInt("level") ?? -1; + } + + @override + void initState() { + super.initState(); + setSelectedLevel(); + } + @override Widget build(BuildContext context) { return BlocProvider( @@ -63,149 +81,178 @@ class Home extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - height: MediaQuery.of(context).size.height * 0.25, - width: MediaQuery.of(context).size.width, - color: ColorBase.kPrimaryColor, - ), Stack( overflow: Overflow.visible, children: [ Container( - height: 90, + height: MediaQuery.of(context).size.height * 0.4, width: MediaQuery.of(context).size.width, + color: ColorBase.kPrimaryColor, ), - Positioned( - top: -90, - child: Container( - padding: EdgeInsets.all(10), - width: MediaQuery.of(context).size.width, - child: Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10)), - elevation: 2, - child: Container( - padding: EdgeInsets.all(10), - child: (state is TruthLoaded && - state.truthModel.results == null) || - (state is DareLoaded && - state.dareModel.results == null) - ? Column( - children: [ - Icon(Icons.dangerous,size: 50,color: ColorBase.kPrimaryColor,), - Text( - "Tidak ada Truth or Dare untuk level yang dipilih", - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold), - textAlign: TextAlign.center, - ), - ], - ) - : Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ + Padding( + padding: const EdgeInsets.only(top:100), + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10)), + elevation: 2, + child: Container( + padding: EdgeInsets.all(10), + child: (state is TruthLoaded && + state.truthModel.results == null) || + (state is DareLoaded && + state.dareModel.results == null) + ? Column( + children: [ + Icon( + Icons.dangerous, + size: 50, + color: ColorBase.kPrimaryColor, + ), + Text( + "Tidak ada Truth or Dare untuk level yang dipilih", + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold), + textAlign: TextAlign.center, + ), + ], + ) + : Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + state is TodLoading + ? buildShimmer(context) + : Text( + state is DareLoaded + ? "${capitalize(state.dareModel.results.level)} Dare" + : state is TruthLoaded + ? "${capitalize(state.truthModel.results.level)} Truth" + : "", + style: TextStyle( + fontSize: 24, + fontWeight: + FontWeight.bold), + ), + SizedBox( + height: 10, + ), + Container( + decoration: BoxDecoration( + color: ColorBase.blue, + borderRadius: + BorderRadius.circular(10)), + width: MediaQuery.of(context) + .size + .width, + padding: EdgeInsets.all(20), + child: Column(children: [ state is TodLoading ? buildShimmer(context) : Text( - state is DareLoaded - ? "${capitalize(state.dareModel.results.level)} Dare" - : state is TruthLoaded - ? "${capitalize(state.truthModel.results.level)} Truth" - : "", - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold), - ), - SizedBox( - height: 10, - ), - Container( - decoration: BoxDecoration( - color: ColorBase.blue, - borderRadius: - BorderRadius.circular(10)), - width: - MediaQuery.of(context).size.width, - padding: EdgeInsets.all(20), - child: Column(children: [ - state is TodLoading - ? buildShimmer(context) - : Text( - state is DareLoaded - ? state.dareModel.results - .dare - : state is TruthLoaded - ? state.truthModel - .results.truth - : "", - style: TextStyle( - fontSize: 24, - color: Colors.white), - ), - ]), - ), - SizedBox( - height: 10, - ), - Text( - "Dikirim oleh ${state is DareLoaded ? state.dareModel.results.user.username : state is TruthLoaded ? state.truthModel.results.user.username : ""}", + state is DareLoaded + ? state.dareModel + .results.dare + : state + is TruthLoaded + ? state + .truthModel + .results + .truth + : "", style: TextStyle( - fontSize: 14, color: Colors.grey), - textAlign: TextAlign.end, - ) - ], + fontSize: 24, + color: + Colors.white), + ), + ]), + ), + SizedBox( + height: 10, + ), + Text( + "Dikirim oleh ${state is DareLoaded ? state.dareModel.results.user.username : state is TruthLoaded ? state.truthModel.results.user.username : ""}", + style: TextStyle( + fontSize: 14, + color: Colors.grey), + textAlign: TextAlign.end, ), + ], + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10), + child: Wrap( + direction: Axis.horizontal, + spacing: 10, + children: level.map((e) { + return ChoiceChip( + label: Text(capitalize(e)), + selected: level.indexOf(e) == + selectedLevel, + onSelected: (value) async { + print("Oke"); + if (state is TruthLoaded) { + if (selectedLevel == + level.indexOf(e)) { + BlocProvider.of< + TruthOrDareBloc>( + context) + .add(GetTruth( + selectedLevel: -1)); + selectedLevel = -1; + } else { + BlocProvider.of< + TruthOrDareBloc>( + context) + .add(GetTruth( + selectedLevel: level + .indexOf(e))); + selectedLevel = + level.indexOf(e); + } + } else if (state + is DareLoaded) { + if (selectedLevel == + level.indexOf(e)) { + BlocProvider.of< + TruthOrDareBloc>( + context) + .add(GetDare( + selectedLevel: -1)); + selectedLevel = -1; + } else { + BlocProvider.of< + TruthOrDareBloc>( + context) + .add(GetDare( + selectedLevel: level + .indexOf(e))); + selectedLevel = + level.indexOf(e); + } + await Preferences.setDataInt( + "level", selectedLevel); + setState(() {}); + } + }, + ); + }).toList(), + ), + ), + buildButtonBar(context, state) + ], ), ), ), ) ], ), - state is TruthLoaded || state is DareLoaded - ? Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Wrap( - direction: Axis.horizontal, - spacing: 10, - children: level.map((e) { - return ChoiceChip( - label: Text(capitalize(e)), - selected: state is TruthLoaded - ? state.selectedLevel == level.indexOf(e) - : state is DareLoaded - ? state.selectedLevel == level.indexOf(e) - : false, - onSelected: (value) { - if (state is TruthLoaded) { - if (state.selectedLevel == level.indexOf(e)) { - BlocProvider.of(context) - .add(GetTruth(selectedLevel: -1)); - } else { - BlocProvider.of(context) - .add(GetTruth( - selectedLevel: level.indexOf(e))); - } - } else if (state is DareLoaded) { - if (state.selectedLevel == level.indexOf(e)) { - BlocProvider.of(context) - .add(GetDare(selectedLevel: -1)); - } else { - BlocProvider.of(context) - .add(GetDare( - selectedLevel: level.indexOf(e))); - } - } - }, - ); - }).toList(), - ), - ) - : SizedBox( - height: 10, - ), - buildButtonBar(context,state) + ], ), ), @@ -223,7 +270,7 @@ class Home extends StatelessWidget { highlightColor: Colors.grey[100]); } - Widget buildButtonBar(context,state) { + Widget buildButtonBar(context, state) { return Container( padding: EdgeInsets.all(10), child: Column( @@ -234,17 +281,26 @@ class Home extends StatelessWidget { child: FlatButton( color: ColorBase.kPrimaryColor, onPressed: () { - BlocProvider.of(context).add(GetTruth(selectedLevel:state is TruthLoaded? state.selectedLevel:-1)); + BlocProvider.of(context).add(GetTruth( + selectedLevel: + state is TruthLoaded ? state.selectedLevel : -1)); }, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - state is TruthLoaded?Row( - children: [ - Icon(Icons.check_circle,color: Colors.white,), - SizedBox(width: 10,), - ], - ):SizedBox(), + state is TruthLoaded + ? Row( + children: [ + Icon( + Icons.check_circle, + color: Colors.white, + ), + SizedBox( + width: 10, + ), + ], + ) + : SizedBox(), Text( "Truth", style: TextStyle( @@ -266,17 +322,26 @@ class Home extends StatelessWidget { child: OutlineButton( borderSide: BorderSide(color: ColorBase.kPrimaryColor), onPressed: () { - BlocProvider.of(context).add(GetDare(selectedLevel:state is DareLoaded? state.selectedLevel:-1)); + BlocProvider.of(context).add(GetDare( + selectedLevel: + state is DareLoaded ? state.selectedLevel : -1)); }, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - state is DareLoaded?Row( - children: [ - Icon(Icons.check_circle,color: ColorBase.kPrimaryColor,), - SizedBox(width: 10,), - ], - ):SizedBox(), + state is DareLoaded + ? Row( + children: [ + Icon( + Icons.check_circle, + color: ColorBase.kPrimaryColor, + ), + SizedBox( + width: 10, + ), + ], + ) + : SizedBox(), Text( "Dare", style: TextStyle( diff --git a/lib/screen/truth_or_dare/user_dare.dart b/lib/screen/truth_or_dare/user_dare.dart index bab68d8..cde766c 100644 --- a/lib/screen/truth_or_dare/user_dare.dart +++ b/lib/screen/truth_or_dare/user_dare.dart @@ -35,8 +35,7 @@ class UserDare extends StatelessWidget { ], ), )); - } - else if(state is TodDeleted){ + } else if (state is TodDeleted) { return Scaffold.of(context).showSnackBar(SnackBar( backgroundColor: Theme.of(context).primaryColor, content: Row( @@ -62,8 +61,10 @@ class UserDare extends StatelessWidget { return Center( child: CircularProgressIndicator(), ); - }else if(state is UserDareloaded){ - return BuildUserDare(state: state,); + } else if (state is UserDareloaded) { + return BuildUserDare( + state: state, + ); } return Center( child: CircularProgressIndicator(), @@ -77,8 +78,10 @@ class UserDare extends StatelessWidget { class BuildUserDare extends StatefulWidget { final UserDareloaded state; + const BuildUserDare({ - Key key, this.state, + Key key, + this.state, }) : super(key: key); @override @@ -86,27 +89,69 @@ class BuildUserDare extends StatefulWidget { } class _BuildUserDareState extends State { + ScrollController _scrollController; + + @override + void initState() { + super.initState(); + _scrollController = ScrollController(); + } + + @override + void dispose() { + super.dispose(); + _scrollController.dispose(); + } + @override Widget build(BuildContext context) { - return ListView.builder( - itemCount: widget.state.userDare.length, - shrinkWrap: true, - itemBuilder: (context,index)=>Column( - children: [ - ListTile( - leading: Icon(Icons.directions_run), - title: Text(widget.state.userDare[index].dare), - subtitle: Text(widget.state.userDare[index].level), - trailing: InkWell( - child: Icon(Icons.delete_outline,color: ColorBase.darkRed,), - onTap: (){ - BlocProvider.of(context).add(DeleteUserTod(type: "dare",uuid:widget.state.userDare[index].uuid)); - }, - ), - ), - Divider() - ], - ), - ); + return widget.state.userDare.isEmpty + ? Center( + child: Text("Dare kamu kosong"), + ) + : ListView.builder( + controller: _scrollController + ..addListener(() { + final maxScroll = _scrollController.position.maxScrollExtent; + final currentScroll = _scrollController.position.pixels; + if (!widget.state.hasReachedMax) { + if (maxScroll == currentScroll) { + BlocProvider.of(context).add( + GetMoreUserDare(currentPage: widget.state.currentPage)); + } + } + }), + itemCount: widget.state.hasReachedMax + ? widget.state.userDare.length + : widget.state.userDare.length + 1, + shrinkWrap: true, + itemBuilder: (context, index) => index >= + widget.state.userDare.length + ? Center( + child: CircularProgressIndicator(), + ) + : Column( + children: [ + ListTile( + leading: Icon(Icons.directions_run), + title: Text(widget.state.userDare[index].dare), + subtitle: Text(widget.state.userDare[index].level), + trailing: InkWell( + child: Icon( + Icons.delete_outline, + color: ColorBase.darkRed, + ), + onTap: () { + BlocProvider.of(context).add( + DeleteUserTod( + type: "dare", + uuid: widget.state.userDare[index].uuid)); + }, + ), + ), + Divider() + ], + ), + ); } } diff --git a/lib/screen/truth_or_dare/user_tod.dart b/lib/screen/truth_or_dare/user_tod.dart index 3b71f04..4e1893f 100644 --- a/lib/screen/truth_or_dare/user_tod.dart +++ b/lib/screen/truth_or_dare/user_tod.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:truthordare/constants/Colors.dart'; +import 'package:truthordare/constants/route_paths.dart'; import 'package:truthordare/screen/truth_or_dare/user_dare.dart'; import 'package:truthordare/screen/truth_or_dare/user_truth.dart'; @@ -14,6 +15,9 @@ class UserTod extends StatelessWidget { length: 2, initialIndex: index, child: Scaffold( + floatingActionButton: FloatingActionButton(child: Icon(Icons.add),onPressed: (){ + Navigator.pushNamed(context, submitTodRoute); + },), appBar: AppBar( centerTitle: true, title: Text("List"), diff --git a/lib/screen/truth_or_dare/user_truth.dart b/lib/screen/truth_or_dare/user_truth.dart index 9c50ab3..6009f0f 100644 --- a/lib/screen/truth_or_dare/user_truth.dart +++ b/lib/screen/truth_or_dare/user_truth.dart @@ -86,12 +86,39 @@ class BuildListTruth extends StatefulWidget { } class _BuildListTruthState extends State { + ScrollController _scrollController; + @override + void initState() { + super.initState(); + _scrollController=ScrollController(); + } + @override + void dispose() { + super.dispose(); + _scrollController.dispose(); + } @override Widget build(BuildContext context) { - return ListView.builder( - itemCount: widget.state.userTruth.length, + return widget.state.userTruth.isEmpty?Center(child: Text("Truth kamu kosong"),):ListView.builder( + controller: _scrollController..addListener(() { + final maxScroll = _scrollController.position.maxScrollExtent; + final currentScroll = _scrollController.position.pixels; + if(!widget.state.hasReachedMax){ + if(maxScroll==currentScroll){ + BlocProvider.of(context).add(GetMoreUserTruth(currentPage: widget.state.currentPage)); + } + } + }), + itemCount: widget.state.hasReachedMax + ? widget.state.userTruth.length + : widget.state.userTruth.length + 1, shrinkWrap: true, - itemBuilder: (context,index)=>Column( + itemBuilder: (context,index)=>index >= + widget.state.userTruth.length + ? Center( + child: CircularProgressIndicator(), + ) + :Column( children: [ ListTile( leading: Icon(Icons.directions_run),