diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index cdbf5e5..b6c1eb1 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -6,11 +6,15 @@
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
+
+
+
AyaModel.fromJson(json), url, postParams);
}
+
+ Future getTangheemType() async {
+ String url = "${ApiConsts.tangheemUsers}TangheemType_Get";
+ var postParams = {};
+ return await ApiClient().postJsonForObject((json) => TangheemType.fromJson(json), url, postParams);
+ }
+
+ Future contactUs(String firstName, String lastName, String email, String phone, String description) async {
+ String url = "${ApiConsts.tangheemUsers}ContactUs_Add";
+ var postParams = {"firstName": firstName, "lastName": lastName, "email": email, "phone": phone, "description": description};
+ return await ApiClient().postJsonForObject((json) => GeneralResponseModel.fromJson(json), url, postParams);
+ }
+
+ Future getCountry() async {
+ String url = "${ApiConsts.tangheemUsers}Country_Get";
+ var postParams = {};
+ return await ApiClient().postJsonForObject((json) => CountryModel.fromJson(json), url, postParams);
+ }
}
diff --git a/lib/api/user_api_client.dart b/lib/api/user_api_client.dart
index 3a3e3ef..1696b14 100644
--- a/lib/api/user_api_client.dart
+++ b/lib/api/user_api_client.dart
@@ -1,4 +1,5 @@
import 'package:tangheem/classes/consts.dart';
+import 'package:tangheem/models/country_model.dart';
import 'package:tangheem/models/general_response_model.dart';
import 'api_client.dart';
@@ -12,6 +13,7 @@ class UserApiClient {
String _lastName,
String _email,
String _password,
+ String _countryCode,
String _phone,
) async {
String url = "${ApiConsts.user}UserRegistration_Add";
@@ -22,7 +24,7 @@ class UserApiClient {
"secondName": "",
"thirdName": "",
"lastName": _lastName,
- "countryCode": "string",
+ "countryCode": _countryCode,
"mobileNumber": _phone,
"isUserLock": false,
"gender": 0,
@@ -52,4 +54,10 @@ class UserApiClient {
var postParams = {"email": _email, "opt": _otp, "newPassword": _password, "confirmPassword": _password};
return await ApiClient().postJsonForObject((json) => GeneralResponseModel.fromJson(json), url, postParams);
}
+
+ Future getCountry() async {
+ String url = "${ApiConsts.user}Country_Get";
+ var postParams = {};
+ return await ApiClient().postJsonForObject((json) => CountryModel.fromJson(json), url, postParams);
+ }
}
diff --git a/lib/models/country_model.dart b/lib/models/country_model.dart
new file mode 100644
index 0000000..4d97064
--- /dev/null
+++ b/lib/models/country_model.dart
@@ -0,0 +1,65 @@
+class CountryModel {
+ int totalItemsCount;
+ int statusCode;
+ String message;
+ List data;
+
+ CountryModel(
+ {this.totalItemsCount, this.statusCode, this.message, this.data});
+
+ CountryModel.fromJson(Map json) {
+ totalItemsCount = json['totalItemsCount'];
+ statusCode = json['statusCode'];
+ message = json['message'];
+ if (json['data'] != null) {
+ data = new List();
+ json['data'].forEach((v) {
+ data.add(new Data.fromJson(v));
+ });
+ }
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['totalItemsCount'] = this.totalItemsCount;
+ data['statusCode'] = this.statusCode;
+ data['message'] = this.message;
+ if (this.data != null) {
+ data['data'] = this.data.map((v) => v.toJson()).toList();
+ }
+ return data;
+ }
+}
+
+class Data {
+ int countryId;
+ String countryCode;
+ String countryNameEn;
+ String countryNameAr;
+ String countryFlag;
+
+ Data(
+ {this.countryId,
+ this.countryCode,
+ this.countryNameEn,
+ this.countryNameAr,
+ this.countryFlag});
+
+ Data.fromJson(Map json) {
+ countryId = json['countryId'];
+ countryCode = json['countryCode'];
+ countryNameEn = json['countryNameEn'];
+ countryNameAr = json['countryNameAr'];
+ countryFlag = json['countryFlag'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['countryId'] = this.countryId;
+ data['countryCode'] = this.countryCode;
+ data['countryNameEn'] = this.countryNameEn;
+ data['countryNameAr'] = this.countryNameAr;
+ data['countryFlag'] = this.countryFlag;
+ return data;
+ }
+}
diff --git a/lib/models/tangheem_type_model.dart b/lib/models/tangheem_type_model.dart
new file mode 100644
index 0000000..ea9d9ce
--- /dev/null
+++ b/lib/models/tangheem_type_model.dart
@@ -0,0 +1,54 @@
+class TangheemType {
+ int totalItemsCount;
+ int statusCode;
+ String message;
+ List data;
+
+ TangheemType(
+ {this.totalItemsCount, this.statusCode, this.message, this.data});
+
+ TangheemType.fromJson(Map json) {
+ totalItemsCount = json['totalItemsCount'];
+ statusCode = json['statusCode'];
+ message = json['message'];
+ if (json['data'] != null) {
+ data = new List();
+ json['data'].forEach((v) {
+ data.add(new Data.fromJson(v));
+ });
+ }
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['totalItemsCount'] = this.totalItemsCount;
+ data['statusCode'] = this.statusCode;
+ data['message'] = this.message;
+ if (this.data != null) {
+ data['data'] = this.data.map((v) => v.toJson()).toList();
+ }
+ return data;
+ }
+}
+
+class Data {
+ String tangheemTypeId;
+ String tangheemTypeName;
+ bool isActive;
+
+ Data({this.tangheemTypeId, this.tangheemTypeName, this.isActive});
+
+ Data.fromJson(Map json) {
+ tangheemTypeId = json['tangheemTypeId'];
+ tangheemTypeName = json['tangheemTypeName'];
+ isActive = json['isActive'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['tangheemTypeId'] = this.tangheemTypeId;
+ data['tangheemTypeName'] = this.tangheemTypeName;
+ data['isActive'] = this.isActive;
+ return data;
+ }
+}
diff --git a/lib/ui/bottom_sheets/country_selection_bottom_sheet.dart b/lib/ui/bottom_sheets/country_selection_bottom_sheet.dart
new file mode 100644
index 0000000..ca8355d
--- /dev/null
+++ b/lib/ui/bottom_sheets/country_selection_bottom_sheet.dart
@@ -0,0 +1,104 @@
+import 'package:flutter/material.dart';
+import 'package:tangheem/classes/colors.dart';
+import 'package:tangheem/models/country_model.dart';
+import 'package:tangheem/widgets/common_textfield_widget.dart';
+
+class CountrySelectionBottomSheet extends StatefulWidget {
+ final List countryList;
+ final Function(Data) onSelectCountry;
+ CountrySelectionBottomSheet({Key key, this.countryList, this.onSelectCountry}) : super(key: key);
+
+ @override
+ _CountrySelectionBottomSheetState createState() {
+ return _CountrySelectionBottomSheetState();
+ }
+}
+
+class _CountrySelectionBottomSheetState extends State {
+ TextEditingController _searchCountryController = TextEditingController();
+ List _filteredCountryList = [];
+
+ @override
+ void initState() {
+ super.initState();
+ _searchCountryController.addListener(_onTextChange);
+ _filterList("");
+ }
+
+ void _filterList(String _query) {
+ _filteredCountryList = [];
+ if (_query.isEmpty) {
+ _filteredCountryList = widget.countryList;
+ } else {
+ _filteredCountryList = widget.countryList.where((element) => element.countryNameAr.contains(_query) || element.countryNameEn.toLowerCase().contains(_query.toLowerCase()))?.toList() ?? [];
+ }
+ setState(() {});
+ }
+
+ void _onTextChange() {
+ var _searchText = _searchCountryController.text;
+ print("_searchText:$_searchText");
+ _filterList(_searchText);
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Directionality(
+ textDirection: TextDirection.rtl,
+ child: Container(
+ height: MediaQuery.of(context).size.height * 0.75,
+ padding: EdgeInsets.all(16),
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(16),
+ topRight: Radius.circular(16),
+ ),
+ ),
+ child: Column(
+ children: [
+ Container(
+ padding: EdgeInsets.all(8),
+ height: 54,
+ decoration: BoxDecoration(
+ color: ColorConsts.primaryBlue,
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(12),
+ topRight: Radius.circular(12),
+ bottomRight: Radius.circular(12),
+ bottomLeft: Radius.circular(12),
+ ),
+ ),
+ // color: Const.primaryBlue,
+ child: CommonTextFieldWidget(hint: "البحث في البلد", controller: _searchCountryController),
+ ),
+ Expanded(
+ child: ListView.separated(
+ padding: EdgeInsets.only(left: 8, right: 8),
+ itemCount: _filteredCountryList.length,
+ physics: BouncingScrollPhysics(),
+ separatorBuilder: (context, index) => Divider(
+ height: 1,
+ color: Colors.black87.withOpacity(0.3),
+ ),
+ itemBuilder: (context, index) => ListTile(
+ title: Text(_filteredCountryList[index].countryNameAr + " (" + _filteredCountryList[index].countryCode + ")"),
+ dense: true,
+ onTap: () {
+ Navigator.pop(context);
+ widget.onSelectCountry(_filteredCountryList[index]);
+ },
+ ),
+ ),
+ )
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/ui/screens/login_screen.dart b/lib/ui/screens/login_screen.dart
index 18846fa..45da454 100644
--- a/lib/ui/screens/login_screen.dart
+++ b/lib/ui/screens/login_screen.dart
@@ -37,10 +37,10 @@ class _LoginScreenState extends State {
super.dispose();
}
- void performLogin(String email, String password) async {
+ void performLogin(String _email, String _password) async {
Utils.showLoading(context);
try {
- _authenticationUser = await AuthenticationApiClient().authenticateUser(email, password);
+ _authenticationUser = await AuthenticationApiClient().authenticateUser(_email, _password);
Utils.showToast("Login successfully");
} catch (ex, tr) {
Utils.handleException(ex, null);
diff --git a/lib/ui/screens/registration_screen.dart b/lib/ui/screens/registration_screen.dart
index 2a0fd94..e6cc260 100644
--- a/lib/ui/screens/registration_screen.dart
+++ b/lib/ui/screens/registration_screen.dart
@@ -4,6 +4,8 @@ import 'package:flutter_svg/svg.dart';
import 'package:tangheem/api/user_api_client.dart';
import 'package:tangheem/classes/colors.dart';
import 'package:tangheem/classes/utils.dart';
+import 'package:tangheem/models/country_model.dart';
+import 'package:tangheem/ui/bottom_sheets/country_selection_bottom_sheet.dart';
import 'package:tangheem/widgets/common_textfield_widget.dart';
import 'package:tangheem/extensions/email_validator.dart';
@@ -19,24 +21,32 @@ class RegistrationScreen extends StatefulWidget {
}
class _RegistrationScreenState extends State {
- TextEditingController _usernameController = TextEditingController();
- TextEditingController _fullNameController = TextEditingController();
+ TextEditingController _firstNameController = TextEditingController();
+ TextEditingController _lastNameController = TextEditingController();
TextEditingController _emailController = TextEditingController();
TextEditingController _mobileNumberController = TextEditingController();
TextEditingController _passwordController = TextEditingController();
TextEditingController _confirmPasswordController = TextEditingController();
+
bool _isAccept = false;
+ Data _selectedCountry;
+ CountryModel _countryModel;
+
@override
void initState() {
super.initState();
+ fetchCountryList();
}
- void registerUser(String email, String password) async {
+ void fetchCountryList() async {
Utils.showLoading(context);
try {
- await UserApiClient().registerUser("", "", email, password, _mobileNumberController.text);
- Utils.showToast("Register successfully");
+ _countryModel = await UserApiClient().getCountry();
+ if ((_countryModel?.data?.length ?? 0) > 0) {
+ _selectedCountry = _countryModel.data.first;
+ }
+ setState(() {});
} catch (ex, tr) {
Utils.handleException(ex, null);
} finally {
@@ -44,6 +54,19 @@ class _RegistrationScreenState extends State {
}
}
+ void registerUser(String _firstName, String _lastName, String _email, String _password, String _countryCode, String _phone) async {
+ Utils.showLoading(context);
+ try {
+ await UserApiClient().registerUser(_firstName, _lastName, _email, _password, _countryCode, _phone);
+ Utils.showToast("Register successfully");
+ Utils.hideLoading(context);
+ Navigator.pop(context);
+ } catch (ex, tr) {
+ Utils.handleException(ex, null);
+ Utils.hideLoading(context);
+ }
+ }
+
@override
void dispose() {
super.dispose();
@@ -93,22 +116,26 @@ class _RegistrationScreenState extends State {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
- CommonTextFieldWidget(hint: "اسم المستخدم", controller: _usernameController),
+ CommonTextFieldWidget(hint: "الاسم الاول", controller: _firstNameController),
SizedBox(height: 8),
- CommonTextFieldWidget(hint: "الايميل المسجل", controller: _fullNameController),
+ CommonTextFieldWidget(hint: "اسم النهاية", controller: _lastNameController),
SizedBox(height: 8),
CommonTextFieldWidget(hint: "الايميل", controller: _emailController),
SizedBox(height: 8),
CommonTextFieldWidget(
- hint: "البلد",
- controller: _mobileNumberController,
- showSuffix: true,
+ hint: _selectedCountry?.countryNameAr ?? "البلد",
+ controller: null,
+ suffixWidget: Icon(Icons.keyboard_arrow_down, size: 18, color: ColorConsts.secondaryOrange),
onTap: () {
_selectCountry(context);
},
),
SizedBox(height: 8),
- CommonTextFieldWidget(hint: " رقم الجوال (9xx xxxxxxxxx+)", controller: _mobileNumberController),
+ CommonTextFieldWidget(
+ hint: " رقم الجوال${" (" + (_selectedCountry?.countryCode ?? "+9xx") + " xxxxxxxxx)"}",
+ controller: _mobileNumberController,
+ suffixWidget: Text(_selectedCountry?.countryCode ?? "", textDirection: TextDirection.ltr),
+ ),
SizedBox(height: 8),
CommonTextFieldWidget(hint: "تعيين كلمة المرور", controller: _passwordController),
SizedBox(height: 8),
@@ -141,11 +168,11 @@ class _RegistrationScreenState extends State {
height: 50,
child: TextButton(
onPressed: () {
- if (_usernameController.text.length < 1) {
+ if (_firstNameController.text.length < 1) {
Utils.showToast("Username is empty.");
return;
}
- if (_fullNameController.text.length < 1) {
+ if (_lastNameController.text.length < 1) {
Utils.showToast("Name is empty.");
return;
}
@@ -153,6 +180,10 @@ class _RegistrationScreenState extends State {
Utils.showToast("Email is empty.");
return;
}
+ if (_selectedCountry?.countryCode == null) {
+ Utils.showToast("you must select country.");
+ return;
+ }
if (_mobileNumberController.text.length < 1) {
Utils.showToast("Phone number is empty.");
return;
@@ -180,7 +211,8 @@ class _RegistrationScreenState extends State {
Utils.showToast("You must accept statement to proceed.");
return;
}
- Utils.showToast("اعادة تعيين كلمة المرور");
+ registerUser(
+ _firstNameController.text, _lastNameController.text, _emailController.text, _passwordController.text, _selectedCountry?.countryCode, _mobileNumberController.text);
},
style: TextButton.styleFrom(
primary: Colors.white,
@@ -205,42 +237,23 @@ class _RegistrationScreenState extends State {
void _selectCountry(context) {
showModalBottomSheet(
- context: context,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.only(
- topLeft: Radius.circular(16),
- topRight: Radius.circular(16),
- ),
+ context: context,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.only(
+ topLeft: Radius.circular(16),
+ topRight: Radius.circular(16),
),
- backgroundColor: Colors.transparent,
- builder: (BuildContext bc) {
- return Container(
- height: MediaQuery.of(context).size.height * 0.75,
- // padding: EdgeInsets.all(16),
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.only(
- topLeft: Radius.circular(16),
- topRight: Radius.circular(16),
- ),
- ),
- child: Column(
- children: [
- Container(
- padding: EdgeInsets.all(16),
- decoration: BoxDecoration(
- color: ColorConsts.primaryBlue,
- borderRadius: BorderRadius.only(
- topLeft: Radius.circular(16),
- topRight: Radius.circular(16),
- ),
- ),
- // color: Const.primaryBlue,
- child: CommonTextFieldWidget(hint: "تعيين كلمة المرور", controller: null),
- ),
- Expanded(child: ListView())
- ],
- ));
- });
+ ),
+ backgroundColor: Colors.transparent,
+ builder: (BuildContext bc) => CountrySelectionBottomSheet(
+ countryList: _countryModel?.data ?? [],
+ onSelectCountry: (country) {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ setState(() {
+ _selectedCountry = country;
+ });
+ });
+ }),
+ );
}
}
diff --git a/lib/ui/screens/surah_screen.dart b/lib/ui/screens/surah_screen.dart
index 78a4414..109125e 100644
--- a/lib/ui/screens/surah_screen.dart
+++ b/lib/ui/screens/surah_screen.dart
@@ -25,6 +25,7 @@ class SurahScreen extends StatefulWidget {
}
class _SurahScreenState extends State {
+ GlobalKey _globalKey = GlobalKey();
int _selectedSurah = 0;
int _selectedFromAya = 0;
int _selectedToAya = 0;
@@ -100,6 +101,8 @@ class _SurahScreenState extends State {
@override
Widget build(BuildContext context) {
+ String _surahAya = _ayaModel?.data?.map((e) => e.ayahText)?.toList()?.fold("", (value, element) => value + element) ?? "";
+
return Container(
padding: EdgeInsets.fromLTRB(16, 24, 16, 0),
width: double.infinity,
@@ -181,30 +184,40 @@ class _SurahScreenState extends State {
],
),
Expanded(
- child: ListView(
+ child: SingleChildScrollView(
physics: BouncingScrollPhysics(),
- padding: EdgeInsets.only(top: 16, bottom: 8),
- children: [
- Text(
- "بسم الله الرحمن الرحيم",
- textAlign: TextAlign.center,
- 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(
- _ayaModel?.data?.map((e) => e.ayahText)?.toList()?.fold("", (value, element) => value + element) ?? "",
- textAlign: TextAlign.center,
- style: TextStyle(
- fontFamily: "UthmanicHafs",
- color: ColorConsts.primaryBlue,
- fontSize: 18,
- fontWeight: FontWeight.bold,
- ),
+ child: RepaintBoundary(
+ key: _globalKey,
+ child: Material(
+ color: Colors.white,
+ child: ListView(
+ physics: NeverScrollableScrollPhysics(),
+ shrinkWrap: true,
+ padding: EdgeInsets.only(top: 16, bottom: 8),
+ children: [
+ Text(
+ "بسم الله الرحمن الرحيم",
+ textAlign: TextAlign.center,
+ 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",
+ color: ColorConsts.primaryBlue,
+ fontSize: 18,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ),
+ ],
),
),
- ],
+ ),
),
),
SizedBox(height: 8),
@@ -254,7 +267,7 @@ class _SurahScreenState extends State {
),
),
),
- AyaPlayerWidget(surahName: _surahList.isNotEmpty ? _surahList[_selectedSurah] : null)
+ AyaPlayerWidget(surahName: _surahList.isNotEmpty ? _surahList[_selectedSurah] : null, globalKey: _globalKey)
],
),
);
diff --git a/lib/ui/screens/tangheem_screen.dart b/lib/ui/screens/tangheem_screen.dart
index 76952a5..e7f0f5c 100644
--- a/lib/ui/screens/tangheem_screen.dart
+++ b/lib/ui/screens/tangheem_screen.dart
@@ -16,6 +16,7 @@ class TangheemScreen extends StatefulWidget {
}
class _TangheemScreenState extends State {
+ GlobalKey _globalKey = GlobalKey();
List temp1 = List();
List temp2 = List();
@@ -154,7 +155,7 @@ class _TangheemScreenState extends State {
),
),
),
- AyaPlayerWidget()
+ AyaPlayerWidget(globalKey: null)
],
),
);
diff --git a/lib/widgets/aya_player_widget.dart b/lib/widgets/aya_player_widget.dart
index 3a8f246..f1207be 100644
--- a/lib/widgets/aya_player_widget.dart
+++ b/lib/widgets/aya_player_widget.dart
@@ -1,12 +1,24 @@
+import 'dart:io';
import 'dart:math' as math;
-
+import 'dart:typed_data';
+import 'dart:ui' as ui;
import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
+import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
+import 'package:image_gallery_saver/image_gallery_saver.dart';
+import 'package:just_audio/just_audio.dart';
+import 'package:path_provider/path_provider.dart';
+import 'package:permission_handler/permission_handler.dart';
+import 'package:share/share.dart';
import 'package:tangheem/classes/colors.dart';
+import 'package:tangheem/classes/utils.dart';
+import 'package:volume_controller/volume_controller.dart';
class AyaPlayerWidget extends StatefulWidget {
final String surahName;
- AyaPlayerWidget({Key key, this.surahName}) : super(key: key);
+ final GlobalKey globalKey;
+ AyaPlayerWidget({Key key, this.surahName, @required this.globalKey}) : super(key: key);
@override
_AyaPlayerWidgetState createState() {
@@ -17,14 +29,28 @@ class AyaPlayerWidget extends StatefulWidget {
class _AyaPlayerWidgetState extends State {
double sliderValue = 0.4;
bool isPlaying = false;
+ AudioPlayer _player;
+ bool _isAudioHaveError = false;
@override
void initState() {
super.initState();
+ _player = AudioPlayer();
+ try {
+ final _playlist = ConcatenatingAudioSource(children: [
+ AudioSource.uri(
+ Uri.parse("https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_1MG.mp3"),
+ ),
+ ]);
+ _player.setAudioSource(_playlist, initialIndex: 0, initialPosition: Duration.zero).then((value) => () {});
+ } catch (e) {
+ _isAudioHaveError = true;
+ }
}
@override
void dispose() {
+ _player.dispose();
super.dispose();
}
@@ -82,10 +108,25 @@ class _AyaPlayerWidgetState extends State {
],
),
),
- commonIconButton("assets/icons/download_aya.svg", () {}),
- commonIconButton("assets/icons/share_aya.svg", () {}),
+ commonIconButton("assets/icons/download_aya.svg", () async {
+ if (await _requestPermission()) {
+ if (await _saveAya()) {
+ Utils.showToast("Aya saved successfully");
+ } else {
+ Utils.showToast("Failed to save aya");
+ }
+ } else {
+ Utils.showToast("you must granted permission to download aya.");
+ }
+ }),
+ commonIconButton("assets/icons/share_aya.svg", () {
+ _shareAya();
+ }),
commonIconButton("assets/icons/bookmark.svg", () {}),
- commonIconButton("assets/icons/audio_level.svg", () {}),
+ commonIconButton("assets/icons/audio_level.svg", () async {
+ // var vol = await VolumeController.getVolume();
+ VolumeController.maxVolume();
+ }),
],
),
SizedBox(height: 8),
@@ -94,39 +135,65 @@ class _AyaPlayerWidgetState extends State {
children: [
commonIconButton("assets/icons/next_aya.svg", () {}),
SizedBox(width: 4),
- commonIconButton(isPlaying ? "assets/icons/pause.svg" : "assets/icons/play_aya.svg", () {
- setState(() {
- isPlaying = !isPlaying;
- });
- }),
+ StreamBuilder(
+ stream: _player.playerStateStream,
+ builder: (context, snapshot) {
+ final state = snapshot.data?.playing ?? false;
+
+ if (state) {
+ if (_player.duration.inSeconds == _player.position.inSeconds) {
+ _player.pause();
+ _player.seek(Duration.zero);
+ }
+ }
+ return commonIconButton(state ? "assets/icons/pause.svg" : "assets/icons/play_aya.svg", () {
+ state ? _player.pause() : _player.play();
+ });
+ },
+ ),
SizedBox(width: 4),
commonIconButton("assets/icons/previous_aya.svg", () {}),
SizedBox(width: 16),
Expanded(
- child: SliderTheme(
- data: SliderTheme.of(context).copyWith(
- activeTrackColor: ColorConsts.sliderBackground,
- inactiveTrackColor: ColorConsts.secondaryOrange,
- // trackShape: RoundedRectRangeSliderTrackShape(),
- trackHeight: 8.0,
- thumbColor: ColorConsts.primaryBlack,
- thumbShape: RoundSliderThumbShape(enabledThumbRadius: 10.0),
- overlayColor: Colors.red.withAlpha(32),
- overlayShape: RoundSliderOverlayShape(overlayRadius: 12.0),
- ),
- child: Slider(
- value: sliderValue,
- onChanged: (value) {
- setState(() {
- sliderValue = value;
- });
- },
- ),
+ child: StreamBuilder(
+ stream: _player.positionStream,
+ builder: (context, snapshot) {
+ final state = snapshot.data;
+ return SliderTheme(
+ data: SliderTheme.of(context).copyWith(
+ inactiveTrackColor: ColorConsts.sliderBackground,
+ activeTrackColor: ColorConsts.secondaryOrange,
+ trackHeight: 8.0,
+ thumbColor: ColorConsts.primaryBlack,
+ thumbShape: RoundSliderThumbShape(enabledThumbRadius: 10.0),
+ overlayColor: ColorConsts.primaryBlack.withAlpha(32),
+ overlayShape: RoundSliderOverlayShape(overlayRadius: 12.0),
+ ),
+ child: Directionality(
+ textDirection: TextDirection.ltr,
+ child: Slider(
+ value: (state?.inSeconds ?? 0) + 0.0,
+ min: 0,
+ max: (_player?.duration?.inSeconds ?? 0) + 0.0,
+ onChanged: (value) {
+ _player.seek(Duration(seconds: value.round()));
+ },
+ ),
+ ),
+ );
+ },
),
),
- Text(
- "06:00",
- style: TextStyle(color: ColorConsts.textGrey1, height: 1.1, fontFamily: "Roboto"),
+ SizedBox(width: 8),
+ StreamBuilder(
+ stream: _player.positionStream,
+ builder: (context, snapshot) {
+ final state = snapshot.data;
+ return Text(
+ _durationTime(state) ?? "",
+ style: TextStyle(color: ColorConsts.textGrey1, height: 1.1, fontFamily: "Roboto"),
+ );
+ },
),
],
)
@@ -135,7 +202,64 @@ class _AyaPlayerWidgetState extends State {
);
}
+ String _durationTime(Duration duration) {
+ if (duration == null) {
+ return "00:00";
+ }
+ String twoDigits(int n) => n.toString().padLeft(2, "0");
+ String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
+ String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
+ return "$twoDigitMinutes:$twoDigitSeconds";
+ }
+
Widget commonIconButton(String icon, VoidCallback onPressed) {
- return IconButton(constraints: BoxConstraints(), padding: EdgeInsets.only(right: 2), icon: SvgPicture.asset(icon, height: 16, width: 16), onPressed: onPressed);
+ return IconButton(
+ highlightColor: Colors.transparent,
+ splashColor: Colors.transparent,
+ constraints: BoxConstraints(),
+ padding: EdgeInsets.only(right: 2),
+ icon: SvgPicture.asset(icon, height: 16, width: 16),
+ onPressed: onPressed);
+ }
+
+ Future _requestPermission() async {
+ Map statuses = await [
+ Permission.storage,
+ ].request();
+
+ return statuses[Permission.storage].isGranted;
+ }
+
+ Future _saveAya() async {
+ Utils.showLoading(context);
+ try {
+ RenderRepaintBoundary boundary = widget.globalKey.currentContext.findRenderObject();
+ ui.Image image = await boundary.toImage(pixelRatio: 3.0);
+ ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
+ final result = await ImageGallerySaver.saveImage(byteData.buffer.asUint8List(), quality: 100);
+ Utils.hideLoading(context);
+ return result["isSuccess"];
+ } catch (ex) {
+ Utils.hideLoading(context);
+ return false;
+ }
+ }
+
+ void _shareAya() async {
+ Utils.showLoading(context);
+ try {
+ RenderRepaintBoundary boundary = widget.globalKey.currentContext.findRenderObject();
+ ui.Image image = await boundary.toImage(pixelRatio: 3.0);
+ ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
+ Uint8List pngBytes = byteData.buffer.asUint8List();
+
+ final tempDir = await getTemporaryDirectory();
+ final file = await File('${tempDir.path}/${DateTime.now().toString()}.png').create();
+ await file.writeAsBytes(pngBytes);
+ Utils.hideLoading(context);
+ Share.shareFiles(['${file.path}']);
+ } catch (ex) {
+ Utils.hideLoading(context);
+ }
}
}
diff --git a/lib/widgets/common_textfield_widget.dart b/lib/widgets/common_textfield_widget.dart
index 08d6c8e..562db4a 100644
--- a/lib/widgets/common_textfield_widget.dart
+++ b/lib/widgets/common_textfield_widget.dart
@@ -8,10 +8,10 @@ class CommonTextFieldWidget extends StatelessWidget {
final TextEditingController controller;
final bool isPassword;
final String prefixIcon;
- final bool showSuffix;
+ final Widget suffixWidget;
final Function onTap;
- CommonTextFieldWidget({Key key, @required this.hint, @required this.controller, this.isPassword = false, this.prefixIcon, this.showSuffix = false, this.onTap}) : super(key: key);
+ CommonTextFieldWidget({Key key, @required this.hint, @required this.controller, this.isPassword = false, this.prefixIcon, this.suffixWidget, this.onTap}) : super(key: key);
@override
Widget build(BuildContext context) {
@@ -33,13 +33,20 @@ class CommonTextFieldWidget extends StatelessWidget {
filled: true,
hintStyle: TextStyle(color: ColorConsts.textGrey2, fontSize: 14),
hintText: hint,
- suffixIcon: !showSuffix
+ suffixIcon: suffixWidget == null
? null
: Padding(
padding: EdgeInsets.only(left: 8, right: 0),
- child: Icon(Icons.keyboard_arrow_down, size: 18, color: ColorConsts.secondaryOrange),
+ child: suffixWidget,
),
- suffixIconConstraints: !showSuffix ? null : BoxConstraints(maxHeight: 30),
+
+ // !showSuffix
+ // ? null
+ // : Padding(
+ // padding: EdgeInsets.only(left: 8, right: 0),
+ // child: Icon(Icons.keyboard_arrow_down, size: 18, color: ColorConsts.secondaryOrange),
+ // ),
+ suffixIconConstraints: suffixWidget == null ? null : BoxConstraints(maxHeight: 30),
prefixIconConstraints: prefixIcon == null ? prefixIcon : BoxConstraints(maxHeight: 18),
prefixIcon: prefixIcon == null
? prefixIcon
diff --git a/pubspec.yaml b/pubspec.yaml
index af21aaf..129a415 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -30,6 +30,12 @@ dependencies:
flutter_svg: ^0.19.3
fluttertoast: ^7.1.8
http: ^0.13.0
+ image_gallery_saver: ^1.6.8
+ path_provider: ^2.0.1
+ permission_handler: ^6.1.1
+ share: ^2.0.1
+ just_audio: ^0.7.2
+ volume_controller: ^1.0.1+1
dev_dependencies:
flutter_test: