From 4d793f9416bfc61a74fe6914dcade61b983c5abe Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Sun, 25 Apr 2021 11:05:16 +0300 Subject: [PATCH] bookmark feature added & improvements --- lib/classes/consts.dart | 1 + lib/main.dart | 8 +- lib/models/bookmark_model.dart | 56 ++++++ lib/ui/common_appbar.dart | 9 +- lib/ui/screens/bookmark_screen.dart | 127 +++++++++++++ lib/ui/screens/quran_screen.dart | 237 +++++++++++++++++++------ lib/widgets/aya_player_widget.dart | 7 +- lib/widgets/text_highlight_widget.dart | 4 +- pubspec.yaml | 2 +- 9 files changed, 389 insertions(+), 62 deletions(-) create mode 100644 lib/models/bookmark_model.dart create mode 100644 lib/ui/screens/bookmark_screen.dart diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 362228a..897f46c 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -11,4 +11,5 @@ class GlobalConsts { static String isRememberMe = "remember_me"; static String email = "email"; static String password = "password"; + static String bookmark = "bookmark"; } diff --git a/lib/main.dart b/lib/main.dart index 7fda270..463f518 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:tangheem/ui/screens/bookmark_screen.dart'; import 'package:tangheem/ui/screens/contact_us_screen.dart'; import 'ui/common_appbar.dart'; import 'ui/screens/forgot_password_screen.dart'; @@ -43,6 +44,9 @@ class Application extends StatelessWidget { case ContactUsScreen.routeName: className = CommonAppbar(child: ContactUsScreen()); break; + case BookmarkScreen.routeName: + className = CommonAppbar(child: BookmarkScreen()); + break; case TangheemScreen.routeName: Map data = settings.arguments; className = CommonAppbar( @@ -59,8 +63,8 @@ class Application extends StatelessWidget { ); break; case QuranScreen.routeName: - String query = settings.arguments.toString(); - className = CommonAppbar(child: QuranScreen(query: query)); + var bookMark = settings.arguments; + className = CommonAppbar(child: QuranScreen(bookmark: bookMark)); break; } return goToNavigation(className); diff --git a/lib/models/bookmark_model.dart b/lib/models/bookmark_model.dart new file mode 100644 index 0000000..c68d7da --- /dev/null +++ b/lib/models/bookmark_model.dart @@ -0,0 +1,56 @@ +import 'dart:convert'; + +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:tangheem/classes/consts.dart'; + +class BookMarkModel { + int surahID; + String surahNameAR; + int ayahID; + int numberInSurah; + int page; + String ayahText; + + BookMarkModel({this.surahID, this.surahNameAR, this.ayahID, this.numberInSurah, this.page, this.ayahText}); + + BookMarkModel.fromJson(Map json) { + surahID = json['surahID']; + surahNameAR = json['surahNameAR']; + ayahID = json['ayahID']; + numberInSurah = json['numberInSurah']; + page = json['page']; + ayahText = json['ayah_Text']; + } + + Map toJson() { + final Map data = new Map(); + data['surahID'] = this.surahID; + data['surahNameAR'] = this.surahNameAR; + data['ayahID'] = this.ayahID; + data['numberInSurah'] = this.numberInSurah; + data['page'] = this.page; + data['ayah_Text'] = this.ayahText; + return data; + } + + String reverseAyatNumber() { + String _ayaTemp = ayahText.substring(0, ayahText.length - numberInSurah.toString().length); + String _ayaNum = ayahText.substring(ayahText.length - numberInSurah.toString().length, ayahText.length); + _ayaNum = _ayaNum.split('').reversed.join(''); + return "$_ayaTemp$_ayaNum"; + } + + static Future> getFromPrefs() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + List encodedList = prefs.getStringList(GlobalConsts.bookmark) ?? []; + return encodedList.map((e) => BookMarkModel.fromJson(jsonDecode(e))).toList(); + } + + static void saveToPrefs(List list) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + + List encodedList = list.map((e) => jsonEncode(e.toJson())).toList(); + var abc = await prefs.setStringList(GlobalConsts.bookmark, encodedList); + print("saveToPrefs:$abc"); + } +} diff --git a/lib/ui/common_appbar.dart b/lib/ui/common_appbar.dart index 09d3d66..94e1d91 100644 --- a/lib/ui/common_appbar.dart +++ b/lib/ui/common_appbar.dart @@ -5,6 +5,7 @@ import 'package:tangheem/app_state/app_state.dart'; import 'package:tangheem/classes/colors.dart'; import 'package:tangheem/classes/utils.dart'; import 'package:tangheem/models/quick_links_model.dart'; +import 'package:tangheem/ui/screens/bookmark_screen.dart'; import 'package:tangheem/ui/screens/contact_us_screen.dart'; import 'package:tangheem/ui/screens/login_screen.dart'; import 'package:tangheem/ui/screens/quran_screen.dart'; @@ -126,7 +127,9 @@ class _CommonAppbarState extends State { children: [ commonIconButton("assets/icons/accessibility.svg", () {}), commonIconButton("assets/icons/dark_mode.svg", () {}), - commonIconButton("assets/icons/bookmark.svg", () {}), + commonIconButton("assets/icons/bookmark.svg", () { + Navigator.pushNamed(context, BookmarkScreen.routeName); + }), commonIconButton("assets/icons/increase_size.svg", () {}), commonIconButton("assets/icons/reduce_size.svg", () {}), commonIconButton("assets/icons/notification.svg", () {}), @@ -234,8 +237,8 @@ class _CommonAppbarState extends State { ), ), SizedBox(height: 2), - myListItem("assets/icons/read_quran.svg", "اقرأ القرآن الكريم", false, onTap: () async { - await Navigator.pushNamed(context, QuranScreen.routeName, arguments: ""); + myListItem("assets/icons/read_quran.svg", "اقرأ القرآن الكريم", false, onTap: () { + Navigator.pushNamed(context, QuranScreen.routeName, arguments: null); }), SizedBox(height: 2), myListItem("assets/icons/contact.svg", "الاتصال بنا", false, onTap: () { diff --git a/lib/ui/screens/bookmark_screen.dart b/lib/ui/screens/bookmark_screen.dart new file mode 100644 index 0000000..7d96fe8 --- /dev/null +++ b/lib/ui/screens/bookmark_screen.dart @@ -0,0 +1,127 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:tangheem/classes/colors.dart'; +import 'package:tangheem/classes/utils.dart'; +import 'package:tangheem/models/bookmark_model.dart'; +import 'package:tangheem/models/surah_model.dart'; +import 'package:tangheem/ui/misc/no_data_ui.dart'; +import 'package:tangheem/ui/screens/quran_screen.dart'; + +class BookmarkScreen extends StatefulWidget { + static const String routeName = "/bookmark"; + final String tangheemQuery; + final String tangheemTypeName; + final SurahModelData surah; + BookmarkScreen({Key key, this.surah, this.tangheemQuery, this.tangheemTypeName}) : super(key: key); + + @override + _BookmarkScreenState createState() { + return _BookmarkScreenState(); + } +} + +class _BookmarkScreenState extends State { + List _bookMarkList = []; + SlidableController _slidableController; + + @override + void initState() { + _slidableController = SlidableController( + onSlideAnimationChanged: handleSlideAnimationChanged, + onSlideIsOpenChanged: handleSlideIsOpenChanged, + ); + super.initState(); + getBookMark(); + } + + void handleSlideAnimationChanged(Animation slideAnimation) { + // setState(() { + // _rotationAnimation = slideAnimation; + // }); + } + + void handleSlideIsOpenChanged(bool isOpen) { + // setState(() { + // _fabColor = isOpen! ? Colors.green : Colors.blue; + // }); + } + void getBookMark() async { + Utils.showLoading(context); + _bookMarkList = await BookMarkModel.getFromPrefs(); + _bookMarkList = _bookMarkList.reversed.toList(); + Utils.hideLoading(context); + setState(() {}); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return _bookMarkList.isEmpty + ? NoDataUI() + : ListView.separated( + physics: BouncingScrollPhysics(), + padding: EdgeInsets.all(16), + itemCount: _bookMarkList.length, + separatorBuilder: (context, index) { + return SizedBox(height: 8); + }, + itemBuilder: (context, index) { + return Slidable( + controller: _slidableController, + actionPane: SlidableBehindActionPane(), + secondaryActions: [ + IconSlideAction( + color: Colors.redAccent, + icon: Icons.delete, + closeOnTap: true, + onTap: () { + _bookMarkList.removeAt(index); + BookMarkModel.saveToPrefs(_bookMarkList); + Utils.showToast("تمت إزالة الإشارة المرجعية"); + setState(() {}); + }, + ), + ], + child: ListTile( + tileColor: Colors.white, + contentPadding: EdgeInsets.fromLTRB(12, 8, 12, 8), + onTap: () { + Navigator.pushNamed(context, QuranScreen.routeName, arguments: _bookMarkList[index]); + }, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + title: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + _bookMarkList[index].surahNameAR + ":", + style: TextStyle(fontSize: 12, color: ColorConsts.primaryBlue), + ), + Text( + " ${_bookMarkList[index].numberInSurah}", + style: TextStyle(fontSize: 14, color: ColorConsts.secondaryOrange), + ), + ], + ), + subtitle: Text( + _bookMarkList[index].reverseAyatNumber(), + textAlign: TextAlign.start, + style: TextStyle( + fontFamily: "UthmanicHafs", + fontSize: 16, + color: ColorConsts.primaryBlue, + fontWeight: FontWeight.bold, + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/ui/screens/quran_screen.dart b/lib/ui/screens/quran_screen.dart index 7a16b92..90a78b8 100644 --- a/lib/ui/screens/quran_screen.dart +++ b/lib/ui/screens/quran_screen.dart @@ -1,4 +1,5 @@ import 'package:flutter/cupertino.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -8,6 +9,7 @@ import 'package:tangheem/classes/colors.dart'; import 'package:tangheem/classes/utils.dart'; import 'package:tangheem/models/aya_model.dart'; import 'package:tangheem/models/aya_tangheem_type_mapped.dart'; +import 'package:tangheem/models/bookmark_model.dart'; import 'package:tangheem/models/surah_model.dart'; import 'package:tangheem/widgets/auto_scroll_view/aya_scroll_view.dart'; import 'package:tangheem/widgets/auto_scroll_view/scroll_id.dart'; @@ -19,13 +21,12 @@ import 'tangheem_detail_screen.dart'; class QuranScreen extends StatefulWidget { static const String routeName = "/surah"; - final String query; - - QuranScreen({Key key, @required this.query}) : super(key: key); + final BookMarkModel bookmark; + QuranScreen({Key key, this.bookmark}) : super(key: key); @override _QuranScreenState createState() { - return _QuranScreenState(); + return _QuranScreenState(bookmark); } } @@ -48,10 +49,13 @@ class _QuranScreenState extends State { List _tangheemWords = []; + _QuranScreenState(this._selectedAyaForBookmark); + @override void initState() { super.initState(); scrollToId = ScrollToId(scrollController: scrollController); + getBookMark(); getSurah(); } @@ -65,15 +69,26 @@ class _QuranScreenState extends State { _surahModel = AppState().getSurahModel; } _surahList = _surahModel.data.map((element) => element.nameAR).toList(); - _currentPage = 1; + _currentPage = widget.bookmark?.page ?? 1; + Utils.hideLoading(context); //filterData(); } catch (ex, tr) { - Utils.handleException(ex, null); Utils.hideLoading(context); + Utils.handleException(ex, null); } setState(() {}); getQuranByPageNo(); + getTangheemBySurahId(); + } + + List _bookMark = []; + void getBookMark() async { + _bookMark = await BookMarkModel.getFromPrefs(); + if (_bookMark.length > 1) { + _selectedAyaForBookmark = _selectedAyaForBookmark ?? _bookMark.last; + } + setState(() {}); } int numberOfAyah = 0; @@ -90,14 +105,14 @@ class _QuranScreenState extends State { } void getTangheemBySurahId() async { - Utils.showLoading(context); + //Utils.showLoading(context); try { _ayatTangheemTypeMapped = await TangheemUserApiClient().getTangheemBySurah(_selectedSurah + 1); _tangheemWords = _ayatTangheemTypeMapped?.data?.map((e) => e.highlightText)?.toList() ?? []; } catch (ex, tr) { Utils.handleException(ex, null); } finally { - Utils.hideLoading(context); + //Utils.hideLoading(context); } setState(() {}); //getAyaByRange(); @@ -188,9 +203,11 @@ class _QuranScreenState extends State { var temp = element.numberInSurah == 1 ? getBismillahWithSurahName(element.surahID, element.surahNameAR, element.surahID != 1, _surahAya.length <= 1) + element.reverseAyatNumber() : element.reverseAyatNumber(); - _surahAya = _surahAya + temp; + _surahAya = _surahAya + temp + " "; }); + _surahAya = _surahAya + "\n" + "$_currentPage"; + return Container( padding: EdgeInsets.fromLTRB(16, 24, 16, 0), width: double.infinity, @@ -241,16 +258,17 @@ class _QuranScreenState extends State { if (_selectedSurah < 0) { Utils.showToast("الرجاء اختيار السورة"); return; + } else { + if (_selectedFromAya < 0 && _selectedToAya < 0) { + _currentPage = _surahModel?.data[_selectedSurah]?.startPageNo; + getQuranByPageNo(); + return; + } + if (_selectedFromAya >= 0 && _selectedToAya >= 0) { + getAyaByRange(); + return; + } } - if (_selectedFromAya < 0) { - Utils.showToast("لم تحدد بداية الآية"); - return; - } - if (_selectedToAya < 0) { - Utils.showToast("لم تحدد الآية المنتهية"); - return; - } - getAyaByRange(); }), SizedBox(width: 8), commonIconButton( @@ -316,41 +334,38 @@ class _QuranScreenState extends State { // style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20, color: ColorConsts.primaryBlue, height: 1), // ), // SizedBox(height: 8), - Container( - padding: EdgeInsets.only(left: 4, right: 4), - child: - - // Text( - // _surahAya, - // textAlign: TextAlign.center, - // style: TextStyle( - // fontFamily: "UthmanicHafs", - // fontSize: 18, - // color: ColorConsts.primaryBlue, - // fontWeight: FontWeight.bold, - // ), - // ), - // todo "sikander" : lines commented for future - TextHighLightWidget( - text: _surahAya, - valueColor: ColorConsts.primaryBlue, - highLightColor: ColorConsts.primaryBlue, - highlights: _tangheemWords, - onTap: (value) { - List _ayatList = _ayatTangheemTypeMapped.data?.where((element) => element.highlightText == value)?.toList() ?? []; - if (_ayatList.length > 1) { - _selectTangheemType(_ayatList); - } else { - Navigator.pushNamed(context, TangheemDetailScreen.routeName, arguments: _ayatList.first); - } - }, - style: TextStyle( - fontFamily: "UthmanicHafs", - fontSize: 18, - fontWeight: FontWeight.bold, + Container(padding: EdgeInsets.only(left: 4, right: 4), child: quranText() + // Text( + // _surahAya, + // textAlign: TextAlign.center, + // style: TextStyle( + // fontFamily: "UthmanicHafs", + // fontSize: 18, + // color: ColorConsts.primaryBlue, + // fontWeight: FontWeight.bold, + // ), + // ), + // todo "sikander" : lines commented for future + // TextHighLightWidget( + // text: _surahAya, + // valueColor: ColorConsts.primaryBlue, + // highLightColor: ColorConsts.primaryBlue, + // highlights: _tangheemWords, + // onTap: (value) { + // List _ayatList = _ayatTangheemTypeMapped.data?.where((element) => element.highlightText == value)?.toList() ?? []; + // if (_ayatList.length > 1) { + // _selectTangheemType(_ayatList); + // } else { + // Navigator.pushNamed(context, TangheemDetailScreen.routeName, arguments: _ayatList.first); + // } + // }, + // style: TextStyle( + // fontFamily: "UthmanicHafs", + // fontSize: 18, + // fontWeight: FontWeight.bold, + // ), + // ), ), - ), - ), ], ), ), @@ -505,4 +520,122 @@ class _QuranScreenState extends State { ), ); } + + BookMarkModel _selectedAyaForBookmark; + + Widget quranText() { + List textSpanList = []; + String _surahAya = ""; + _ayaModel?.data?.forEach((element) { + var temp = element.numberInSurah == 1 + ? getBismillahWithSurahName(element.surahID, element.surahNameAR, element.surahID != 1, _surahAya.length <= 1) + element.reverseAyatNumber() + : element.reverseAyatNumber(); + + textSpanList.add(TextSpan( + text: temp + " ", + style: TextStyle( + backgroundColor: _selectedAyaForBookmark?.ayahID == element.ayahID ? ColorConsts.secondaryOrange.withOpacity(.4) : Colors.transparent, + fontFamily: "UthmanicHafs", + fontSize: 18, + color: ColorConsts.primaryBlue, + fontWeight: FontWeight.bold, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + setState(() { + if (_selectedAyaForBookmark == null) { + _selectedAyaForBookmark = BookMarkModel.fromJson(element.toJson()); + addBookMarkOption(); + return; + } + _selectedAyaForBookmark = null; + }); + }, + )); + + _surahAya = _surahAya + temp + " "; + }); + + textSpanList.add( + TextSpan( + text: "\n" + "$_currentPage", + style: TextStyle( + fontFamily: "UthmanicHafs", + fontSize: 18, + color: ColorConsts.primaryBlue, + fontWeight: FontWeight.bold, + ), + ), + ); + + _surahAya = _surahAya + "\n" + "$_currentPage"; + + return RichText( + textAlign: TextAlign.center, + textWidthBasis: TextWidthBasis.parent, + text: TextSpan( + children: textSpanList, + ), + ); + } + + void addBookMarkOption() { + var isAyaHaveTagheem = _ayatTangheemTypeMapped?.data?.firstWhere((element) => element.ayahNo == _selectedAyaForBookmark.ayahID, orElse: () => null); + showModalBottomSheet( + context: context, + backgroundColor: Colors.transparent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + builder: (context) { + return Directionality( + textDirection: TextDirection.rtl, + child: Container( + margin: EdgeInsets.all(16), + padding: EdgeInsets.symmetric(vertical: 12, horizontal: 12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (isAyaHaveTagheem != null) + InkWell( + onTap: () { + Navigator.pop(context); + List _ayatList = _ayatTangheemTypeMapped.data?.where((element) => element.ayahNo == _selectedAyaForBookmark.ayahID)?.toList() ?? []; + if (_ayatList.length > 1) { + _selectTangheemType(_ayatList); + } else { + Navigator.pushNamed(context, TangheemDetailScreen.routeName, arguments: _ayatList.first); + } + }, + child: Row( + children: [Text("عرض تنغيم")], + ), + ), + if (isAyaHaveTagheem != null) SizedBox(height: 8), + InkWell( + onTap: () { + Navigator.pop(context); + var temp = _bookMark.firstWhere((element) => element.ayahID == _selectedAyaForBookmark.ayahID, orElse: () => null); + if (temp != null) { + Utils.showToast("تمت إضافة إشارة مرجعية بالفعل"); + return; + } + _bookMark.add(_selectedAyaForBookmark); + BookMarkModel.saveToPrefs(_bookMark); + Utils.showToast("وأضاف المرجعية"); + }, + child: Row( + children: [Text("أضف إلى المرجعية")], + ), + ), + ], + ), + ), + ); + }); + } } diff --git a/lib/widgets/aya_player_widget.dart b/lib/widgets/aya_player_widget.dart index 2323abd..9ce9e8a 100644 --- a/lib/widgets/aya_player_widget.dart +++ b/lib/widgets/aya_player_widget.dart @@ -15,7 +15,6 @@ import 'package:share/share.dart'; import 'package:tangheem/classes/colors.dart'; import 'package:tangheem/classes/utils.dart'; import 'package:tangheem/models/aya_tangheem_type_mapped.dart'; -import 'package:volume_controller/volume_controller.dart'; class AyaPlayerWidget extends StatefulWidget { final String surahName; @@ -130,7 +129,9 @@ class _AyaPlayerWidgetState extends State { ? null : DecorationImage( fit: BoxFit.cover, - image: MemoryImage(base64Decode(widget.voiceNoteList?.elementAt(state)?.profilePicture)), + image: MemoryImage( + base64Decode(widget.voiceNoteList.elementAt(state).profilePicture), + ), ), borderRadius: BorderRadius.all( Radius.circular(30.0), @@ -190,7 +191,7 @@ class _AyaPlayerWidgetState extends State { commonIconButton("assets/icons/share_aya.svg", () { _shareAya(); }), - commonIconButton("assets/icons/bookmark.svg", () {}), + // commonIconButton("assets/icons/bookmark.svg", () {}), // commonIconButton("assets/icons/audio_level.svg", () async { // // var vol = await VolumeController.getVolume(); // VolumeController.maxVolume(); diff --git a/lib/widgets/text_highlight_widget.dart b/lib/widgets/text_highlight_widget.dart index 4f3edb0..ba98e80 100644 --- a/lib/widgets/text_highlight_widget.dart +++ b/lib/widgets/text_highlight_widget.dart @@ -12,6 +12,7 @@ class TextHighLightWidget extends StatelessWidget { final TextAlign textAlign; final Color highLightColor; final Function(String) onTap; + final Function(String) onAyaTap; TextHighLightWidget( {Key key, @@ -22,7 +23,8 @@ class TextHighLightWidget extends StatelessWidget { this.highlights, this.highLightColor = ColorConsts.secondaryOrange, this.style = const TextStyle(), - this.onTap}); + this.onTap, + this.onAyaTap}); @override Widget build(BuildContext context) { diff --git a/pubspec.yaml b/pubspec.yaml index b78842c..cf12363 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,10 +35,10 @@ dependencies: permission_handler: ^6.1.1 share: ^2.0.1 just_audio: ^0.7.2 - volume_controller: ^1.0.1+1 intl: ^0.17.0 shared_preferences: ^2.0.5 url_launcher: ^6.0.3 + flutter_slidable: ^0.6.0 dev_dependencies: flutter_test: