download aya to device added, share aya added & play aya as audio added.

development
Sikander Saleem 5 years ago
parent ee5c9a9cd1
commit a810086aef

@ -6,11 +6,15 @@
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:name="io.flutter.app.FlutterApplication"
android:label="Tangheem"
android:roundIcon="@mipmap/ic_launcher_round"
android:usesCleartextTraffic="true"
android:requestLegacyExternalStorage="true"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"

@ -1,5 +1,8 @@
import 'dart:async';
import 'package:tangheem/models/country_model.dart';
import 'package:tangheem/models/general_response_model.dart';
import 'package:tangheem/models/tangheem_type_model.dart';
import 'api_client.dart';
import 'package:tangheem/classes/consts.dart';
import 'package:tangheem/models/aya_model.dart';
@ -21,4 +24,22 @@ class TangheemUserApiClient {
var postParams = {"itemsPerPage": itemsPerPage, "currentPageNo": currentPageNo, "sortFieldName": "string", "isSortAsc": true, "surahID": surahID, "ayahFrom": ayahFrom, "ayahTo": ayahTo};
return await ApiClient().postJsonForObject((json) => AyaModel.fromJson(json), url, postParams);
}
Future<TangheemType> getTangheemType() async {
String url = "${ApiConsts.tangheemUsers}TangheemType_Get";
var postParams = {};
return await ApiClient().postJsonForObject((json) => TangheemType.fromJson(json), url, postParams);
}
Future<GeneralResponseModel> 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<CountryModel> getCountry() async {
String url = "${ApiConsts.tangheemUsers}Country_Get";
var postParams = {};
return await ApiClient().postJsonForObject((json) => CountryModel.fromJson(json), url, postParams);
}
}

@ -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<CountryModel> getCountry() async {
String url = "${ApiConsts.user}Country_Get";
var postParams = {};
return await ApiClient().postJsonForObject((json) => CountryModel.fromJson(json), url, postParams);
}
}

@ -0,0 +1,65 @@
class CountryModel {
int totalItemsCount;
int statusCode;
String message;
List<Data> data;
CountryModel(
{this.totalItemsCount, this.statusCode, this.message, this.data});
CountryModel.fromJson(Map<String, dynamic> json) {
totalItemsCount = json['totalItemsCount'];
statusCode = json['statusCode'];
message = json['message'];
if (json['data'] != null) {
data = new List<Data>();
json['data'].forEach((v) {
data.add(new Data.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
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<String, dynamic> json) {
countryId = json['countryId'];
countryCode = json['countryCode'];
countryNameEn = json['countryNameEn'];
countryNameAr = json['countryNameAr'];
countryFlag = json['countryFlag'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['countryId'] = this.countryId;
data['countryCode'] = this.countryCode;
data['countryNameEn'] = this.countryNameEn;
data['countryNameAr'] = this.countryNameAr;
data['countryFlag'] = this.countryFlag;
return data;
}
}

@ -0,0 +1,54 @@
class TangheemType {
int totalItemsCount;
int statusCode;
String message;
List<Data> data;
TangheemType(
{this.totalItemsCount, this.statusCode, this.message, this.data});
TangheemType.fromJson(Map<String, dynamic> json) {
totalItemsCount = json['totalItemsCount'];
statusCode = json['statusCode'];
message = json['message'];
if (json['data'] != null) {
data = new List<Data>();
json['data'].forEach((v) {
data.add(new Data.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
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<String, dynamic> json) {
tangheemTypeId = json['tangheemTypeId'];
tangheemTypeName = json['tangheemTypeName'];
isActive = json['isActive'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['tangheemTypeId'] = this.tangheemTypeId;
data['tangheemTypeName'] = this.tangheemTypeName;
data['isActive'] = this.isActive;
return data;
}
}

@ -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<Data> countryList;
final Function(Data) onSelectCountry;
CountrySelectionBottomSheet({Key key, this.countryList, this.onSelectCountry}) : super(key: key);
@override
_CountrySelectionBottomSheetState createState() {
return _CountrySelectionBottomSheetState();
}
}
class _CountrySelectionBottomSheetState extends State<CountrySelectionBottomSheet> {
TextEditingController _searchCountryController = TextEditingController();
List<Data> _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]);
},
),
),
)
],
),
),
);
}
}

@ -37,10 +37,10 @@ class _LoginScreenState extends State<LoginScreen> {
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);

@ -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<RegistrationScreen> {
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<RegistrationScreen> {
}
}
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<RegistrationScreen> {
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<RegistrationScreen> {
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<RegistrationScreen> {
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<RegistrationScreen> {
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<RegistrationScreen> {
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;
});
});
}),
);
}
}

@ -25,6 +25,7 @@ class SurahScreen extends StatefulWidget {
}
class _SurahScreenState extends State<SurahScreen> {
GlobalKey _globalKey = GlobalKey();
int _selectedSurah = 0;
int _selectedFromAya = 0;
int _selectedToAya = 0;
@ -100,6 +101,8 @@ class _SurahScreenState extends State<SurahScreen> {
@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<SurahScreen> {
],
),
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<SurahScreen> {
),
),
),
AyaPlayerWidget(surahName: _surahList.isNotEmpty ? _surahList[_selectedSurah] : null)
AyaPlayerWidget(surahName: _surahList.isNotEmpty ? _surahList[_selectedSurah] : null, globalKey: _globalKey)
],
),
);

@ -16,6 +16,7 @@ class TangheemScreen extends StatefulWidget {
}
class _TangheemScreenState extends State<TangheemScreen> {
GlobalKey _globalKey = GlobalKey();
List<TempModel> temp1 = List();
List<TempModel> temp2 = List();
@ -154,7 +155,7 @@ class _TangheemScreenState extends State<TangheemScreen> {
),
),
),
AyaPlayerWidget()
AyaPlayerWidget(globalKey: null)
],
),
);

@ -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<AyaPlayerWidget> {
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<AyaPlayerWidget> {
],
),
),
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<AyaPlayerWidget> {
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<PlayerState>(
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<Duration>(
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<Duration>(
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<AyaPlayerWidget> {
);
}
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<bool> _requestPermission() async {
Map<Permission, PermissionStatus> statuses = await [
Permission.storage,
].request();
return statuses[Permission.storage].isGranted;
}
Future<bool> _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);
}
}
}

@ -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

@ -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:

Loading…
Cancel
Save