tsacdop-podcast-app-android/lib/home/share.dart

470 lines
19 KiB
Dart

import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:line_icons/line_icons.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:tsacdop/home/preview.dart';
import 'package:wc_flutter_share/wc_flutter_share.dart';
import 'package:tuple/tuple.dart';
import '../state/audiostate.dart';
import '../util/context_extension.dart';
import '../util/customslider.dart';
import '../util/pageroute.dart';
final List<BoxShadow> _customShadow = [
BoxShadow(blurRadius: 26, offset: Offset(-6, -6), color: Colors.white),
BoxShadow(
blurRadius: 8,
offset: Offset(2, 2),
color: Colors.grey[600].withOpacity(0.4))
];
final List<BoxShadow> _customShadowNight = [
BoxShadow(
blurRadius: 6,
offset: Offset(-1, -1),
color: Colors.grey[100].withOpacity(0.3)),
BoxShadow(blurRadius: 8, offset: Offset(2, 2), color: Colors.black)
];
String _stringForSeconds(int seconds) {
if (seconds == null) return null;
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
}
class ShareClip extends StatefulWidget {
ShareClip({Key key}) : super(key: key);
@override
_ShareClipState createState() => _ShareClipState();
}
class _ShareClipState extends State<ShareClip> {
int _durationSelected;
int _startPosition;
bool _startConfirm;
List<int> _durationToSelect = [30, 60, 90, 120];
Widget _animatedWidget;
Widget _toastWidget;
@override
void initState() {
super.initState();
_durationSelected = 60;
_startPosition = 0;
_startConfirm = false;
_animatedWidget = Center();
_toastWidget = Center();
}
_formatSeconds(int s) {
switch (s) {
case 30:
return "30sec";
break;
case 60:
return "1min";
break;
case 90:
return "90sec";
break;
case 120:
return "2min";
break;
default:
return '';
break;
}
}
_setShareButton(ShareStatus status, String filePath) {
switch (status) {
case ShareStatus.generate:
_animatedWidget = Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
)),
Padding(
padding: EdgeInsets.symmetric(horizontal: 2),
),
Text('Clipping'),
],
);
_toastWidget = Text('May take one minute',
style: TextStyle(color: const Color(0xff67727d)));
break;
case ShareStatus.download:
_animatedWidget = Text('Loading');
break;
case ShareStatus.complete:
_animatedWidget = Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.share),
Padding(
padding: EdgeInsets.symmetric(horizontal: 2),
),
Text('Share'),
],
);
_toastWidget = Row(
children: [
Text('Preview'),
IconButton(
icon: Icon(LineIcons.play_solid),
onPressed: () {
if (filePath != '')
Navigator.push(
context,
SlideLeftRoute(
page: ClipPreview(
filePath: filePath,
)),
);
}),
],
);
break;
case ShareStatus.undefined:
_animatedWidget = Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(LineIcons.cut_solid),
Padding(
padding: EdgeInsets.symmetric(horizontal: 2),
),
Text('Clip')
],
);
_toastWidget = Center();
break;
case ShareStatus.error:
_animatedWidget = Text('Retry');
_toastWidget = Text('Something wrong happened');
break;
}
}
@override
Widget build(BuildContext context) {
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false);
return Container(
height: 300,
width: double.infinity,
color: context.primaryColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
height: 60.0,
// color: context.primaryColorDark,
alignment: Alignment.centerLeft,
child: Row(
children: <Widget>[
Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
height: 20.0,
// color: context.primaryColorDark,
alignment: Alignment.centerLeft,
child: Text(
'Share Clip',
style: TextStyle(
color: Theme.of(context).accentColor,
fontWeight: FontWeight.bold,
fontSize: 16),
),
),
Spacer(),
Selector<AudioPlayerNotifier, Tuple2<ShareStatus, String>>(
selector: (_, audio) =>
Tuple2(audio.shareStatus, audio.shareFile ?? ''),
builder: (_, data, __) {
_setShareButton(data.item1, data.item2);
return Row(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedSwitcher(
transitionBuilder: (child, animation) =>
ScaleTransition(scale: animation, child: child),
duration: Duration(milliseconds: 500),
child: _toastWidget),
Container(
margin: EdgeInsets.symmetric(horizontal: 10),
alignment: Alignment.center,
height: 40,
width: 120,
decoration: BoxDecoration(
color: context.primaryColor,
borderRadius:
BorderRadius.all(Radius.circular(20)),
border: Border.all(
color: Theme.of(context).brightness ==
Brightness.dark
? Colors.black12
: Colors.white10,
width: 1),
boxShadow: Theme.of(context).brightness ==
Brightness.dark
? _customShadowNight
: _customShadow),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius:
BorderRadius.all(Radius.circular(15)),
onTap: () async {
if (data.item1 == ShareStatus.undefined ||
data.item1 ==
ShareStatus.error) if (_startConfirm)
audio.shareClip(
_startPosition, _durationSelected);
else
Fluttertoast.showToast(
msg: 'Please confirm start position',
gravity: ToastGravity.BOTTOM,
);
else if (data.item1 == ShareStatus.complete) {
File file = File(data.item2);
final Uint8List bytes =
await file.readAsBytes();
await WcFlutterShare.share(
sharePopupTitle: 'share Clip',
fileName: data.item2.split('/').last,
mimeType: 'video/mp4',
bytesOfFile: bytes.buffer.asUint8List());
audio.setShareStatue = ShareStatus.undefined;
}
},
child: SizedBox(
height: 40,
width: 100,
child: Center(
child: AnimatedSwitcher(
transitionBuilder: (child, animation) =>
ScaleTransition(
scale: animation, child: child),
duration: Duration(milliseconds: 700),
child: _animatedWidget)),
),
),
),
),
],
);
},
),
],
),
),
Consumer<AudioPlayerNotifier>(builder: (_, data, __) {
return Container(
padding: EdgeInsets.only(top: 5, left: 10, right: 10),
child: SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor:
Theme.of(context).brightness == Brightness.dark
? Colors.black38
: Colors.grey[400],
inactiveTrackColor: Theme.of(context).primaryColorDark,
trackHeight: 20.0,
trackShape: MyRectangularTrackShape(),
thumbColor: Theme.of(context).accentColor,
thumbShape: MyRoundSliderThumpShape(
enabledThumbRadius: 10.0,
disabledThumbRadius: 10.0,
thumbCenterColor: context.accentColor),
overlayColor: Theme.of(context).accentColor.withAlpha(32),
overlayShape: RoundSliderOverlayShape(overlayRadius: 4.0),
),
child: Slider(
value: data.seekSliderValue,
onChanged: (double val) {
audio.sliderSeek(val);
}),
),
);
}),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 150,
width: 200,
child: Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Center(
child: Selector<AudioPlayerNotifier, int>(
selector: (_, audio) => audio.backgroundAudioPosition,
builder: (_, position, __) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(10.0),
child: Text.rich(
TextSpan(
text: 'Start at \n',
style:
TextStyle(color: context.accentColor),
children: [
TextSpan(
text: !_startConfirm
? _stringForSeconds(
position ~/ 1000)
: _stringForSeconds(
_startPosition),
style: context.textTheme.headline5
.copyWith(
color: _startConfirm
? context.accentColor
: null)),
]),
textAlign: TextAlign.center,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
padding: EdgeInsets.symmetric(
horizontal: 10.0),
onPressed: () => audio.forwardAudio(-30),
iconSize: 25.0,
icon: Icon(Icons.replay_30),
color: Colors.grey[500]),
InkWell(
onTap: () => setState(() {
if (!_startConfirm)
_startPosition = position ~/ 1000;
_startConfirm = !_startConfirm;
}),
child: Container(
margin: EdgeInsets.all(10.0),
decoration: BoxDecoration(
boxShadow: !_startConfirm
? (context.brightness ==
Brightness.dark)
? _customShadowNight
: _customShadow
: null,
color: _startConfirm
? Theme.of(context).accentColor
: Theme.of(context).primaryColor,
shape: BoxShape.circle),
alignment: Alignment.center,
width: 40,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
LineIcons.thumbtack_solid,
color: _startConfirm
? Colors.white
: null,
)),
),
),
IconButton(
padding: EdgeInsets.symmetric(
horizontal: 10.0),
onPressed: () => audio.forwardAudio(10),
iconSize: 25.0,
icon: Icon(Icons.forward_10),
color: Colors.grey[500]),
],
),
],
);
},
),
),
),
),
Container(
height: 100,
width: 2,
color: context.primaryColorDark,
),
SizedBox(
height: 150,
width: 200,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Center(
child: Wrap(
direction: Axis.horizontal,
children: _durationToSelect
.map<Widget>((e) => InkWell(
onTap: () =>
setState(() => _durationSelected = e),
child: Container(
margin: EdgeInsets.all(10.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(15)),
boxShadow: e != _durationSelected
? (context.brightness ==
Brightness.dark)
? _customShadowNight
: _customShadow
: null,
color: (e == _durationSelected)
? Theme.of(context).accentColor
: Theme.of(context).primaryColor,
),
alignment: Alignment.center,
width: 70,
height: 30,
child: Text(_formatSeconds(e),
style: TextStyle(
fontWeight: FontWeight.bold,
color: (e == _durationSelected)
? Colors.white
: null)),
),
))
.toList(),
),
)),
),
],
),
),
Container(
height: 20,
padding: EdgeInsets.symmetric(horizontal: 20),
child: InkWell(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
onTap: () {},
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text('experimental'),
Icon(
LineIcons.info_circle_solid,
size: 20.0,
color: context.accentColor,
),
],
),
),
),
],
),
);
}
}