Skip to content

Commit

Permalink
added a way to auto trim videos longer than 30 secs for statuses
Browse files Browse the repository at this point in the history
  • Loading branch information
netharuM committed Apr 16, 2022
1 parent 7749698 commit 385ab76
Show file tree
Hide file tree
Showing 9 changed files with 788 additions and 50 deletions.
4 changes: 3 additions & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.netharuM.whatskit"
minSdkVersion 16
minSdkVersion 24
targetSdkVersion 30
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
Expand All @@ -55,6 +55,8 @@ android {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
shrinkResources false
minifyEnabled false
}
}
}
Expand Down
54 changes: 52 additions & 2 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:whatskit/pages/statuses_page.dart';
import 'package:whatskit/pages/video_timmer.dart';

void main() {
runApp(const MyApp());
Expand All @@ -15,12 +16,61 @@ class MyApp extends StatelessWidget {
theme: ThemeData(
primarySwatch: generateMaterialColorFromColor(const Color(0xff00a884)),
scaffoldBackgroundColor: const Color(0xff111b21),
backgroundColor: const Color(0xff111b21),
cardColor: const Color(0xff202c33),
primaryColor: const Color(0xff00a884),
textTheme: Theme.of(context).textTheme.apply(bodyColor: Colors.white),
iconTheme: Theme.of(context).iconTheme.copyWith(color: Colors.white),
iconTheme: Theme.of(context)
.iconTheme
.copyWith(color: const Color(0xff00a884)),
bottomNavigationBarTheme: BottomNavigationBarThemeData(
backgroundColor: const Color(0xff202c33),
selectedItemColor: const Color(0xff00a884),
unselectedItemColor: Colors.white.withOpacity(0.5),
),
),
home: const App(),
);
}
}

class App extends StatefulWidget {
const App({Key? key}) : super(key: key);

@override
State<App> createState() => _AppState();
}

class _AppState extends State<App> {
int _pageIndex = 0;

final List<Widget> _pages = [
const StatusesPage(),
const VideoTrimmerPage(),
];

@override
Widget build(BuildContext context) {
return Scaffold(
body: _pages[_pageIndex],
bottomNavigationBar: BottomNavigationBar(
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.video_collection),
label: 'statuses',
),
BottomNavigationBarItem(
icon: Icon(Icons.cut_rounded),
label: 'trimmer',
),
],
currentIndex: _pageIndex,
onTap: (int index) {
setState(() {
_pageIndex = index;
});
},
),
home: const StatusesPage(),
);
}
}
Expand Down
File renamed without changes.
36 changes: 0 additions & 36 deletions lib/pages/statuses_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ class _StatusesPageState extends State<StatusesPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('whatsapp status'),
),
body: RefreshIndicator(
onRefresh: _init,
child: GridView.count(
Expand All @@ -61,39 +58,6 @@ class _StatusesPageState extends State<StatusesPage> {
],
),
),
drawer: Drawer(
backgroundColor: Colors.transparent,
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
topRight: Radius.circular(12),
bottomRight: Radius.circular(12)),
color: Theme.of(context).scaffoldBackgroundColor,
),
child: ListView(
children: [
DrawerHeader(
child: Column(
children: [
Image.asset(
'assets/icon.png',
width: 100,
),
Text(
'whatskit',
style: Theme.of(context).textTheme.headline6,
),
],
),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(12),
),
),
],
),
),
),
);
}
}
234 changes: 234 additions & 0 deletions lib/pages/trimmed_video_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:share_plus/share_plus.dart';
import 'package:video_trimmer/video_trimmer.dart';

class TrimmedVideoPage extends StatefulWidget {
final Duration start;
final Duration end;
final File video;
const TrimmedVideoPage({
Key? key,
required this.video,
this.start = const Duration(seconds: 0),
this.end = const Duration(seconds: 30),
}) : super(key: key);

@override
State<TrimmedVideoPage> createState() => _TrimmedVideoPageState();
}

class _TrimmedVideoPageState extends State<TrimmedVideoPage> {
Trimmer? _trimmer;
bool? _isPlaying;
double? _trimStart;
double? _trimEnd;

@override
void initState() {
_init();
super.initState();
}

Future<void> _init() async {
_trimmer = Trimmer();
await _trimmer!.loadVideo(videoFile: widget.video);
await _trimmer!.videoPlayerController!.setLooping(true);
setState(() {});
}

@override
void dispose() {
_trimmer?.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
if (_trimmer != null) {
return Scaffold(
backgroundColor: Colors.black,
body: Stack(
children: [
Center(
child: GestureDetector(
child: VideoViewer(trimmer: _trimmer!),
onTap: () {
if (_isPlaying ?? false) {
_trimmer!.videoPlayerController!.pause();
} else {
_trimmer!.videoPlayerController!.play();
}
},
),
),
SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TrimEditor(
moveStartPosBy: widget.start.inSeconds.toDouble(),
moveEndPosBy: widget.start.inSeconds.toDouble() >
widget.end.inSeconds.toDouble()
? widget.end.inSeconds.toDouble() -
widget.start.inSeconds.toDouble()
: widget.start.inSeconds.toDouble(),
borderPaintColor: Theme.of(context).primaryColor,
circlePaintColor: Theme.of(context).primaryColor,
thumbnailQuality: 10,
trimmer: _trimmer!,
scrubberPaintColor: Colors.white,
viewerHeight: 50.0,
viewerWidth: MediaQuery.of(context).size.width,
maxVideoLength: const Duration(seconds: 30),
onChangeEnd: (double end) {
setState(() {
_trimEnd = end;
});
},
onChangeStart: (double start) {
setState(() {
_trimStart = start;
});
},
onChangePlaybackState: (playing) {
setState(() {
_isPlaying = playing;
});
},
),
Container(
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Icon(Icons.cancel),
),
TextButton(
onPressed: () {
if (_isPlaying ?? false) {
_trimmer!.videoPlayerController!.pause();
} else {
_trimmer!.videoPlayerController!.play();
}
},
child: _isPlaying ?? false
? const Icon(Icons.pause_circle_filled_rounded)
: const Icon(Icons.play_circle_fill_rounded),
),
TrimmerPosIndicator(
trimmer: _trimmer!,
trimEnd: _trimEnd ??
const Duration(seconds: 30)
.inMilliseconds
.toDouble(),
trimStart: _trimStart ?? 0,
),
TextButton(
onPressed: () async {
_trimmer!.saveTrimmedVideo(
startValue: _trimStart!,
endValue: _trimEnd!,
outputFormat: FileFormat.mp4,
storageDir: StorageDir.temporaryDirectory,
onSave: (String? outputPath) {
Share.shareFiles([outputPath!]);
},
);
},
child: const Icon(Icons.send),
)
],
),
),
],
),
),
],
),
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
}
}

class TrimmerPosIndicator extends StatefulWidget {
final Trimmer trimmer;
final double trimStart;
final double trimEnd;
const TrimmerPosIndicator({
Key? key,
required this.trimmer,
required this.trimStart,
required this.trimEnd,
}) : super(key: key);

@override
State<TrimmerPosIndicator> createState() => _TrimmerPosIndicatorState();
}

class _TrimmerPosIndicatorState extends State<TrimmerPosIndicator> {
double min = 0;
double max = 100;
double position = 0;

void _update() {
setState(() {
double _position = widget
.trimmer.videoPlayerController!.value.position.inMilliseconds
.toDouble();
if (_position >= widget.trimStart && _position <= widget.trimEnd) {
position = _position;
min = widget.trimStart;
max = widget.trimEnd;
} else {
position = widget.trimStart;
min = widget.trimStart;
max = widget.trimEnd;
}
});
}

@override
void initState() {
position = widget
.trimmer.videoPlayerController!.value.position.inMilliseconds
.toDouble();
widget.trimmer.videoPlayerController!.addListener(_update);
super.initState();
}

@override
void dispose() {
widget.trimmer.videoPlayerController!.removeListener(_update);
super.dispose();
}

@override
Widget build(BuildContext context) {
return Expanded(
child: Slider(
value: position,
min: min,
max: max,
onChanged: (double value) {
widget.trimmer.videoPlayerController!.seekTo(
Duration(
milliseconds: value.toInt(),
),
);
},
),
);
}
}
Loading

0 comments on commit 385ab76

Please sign in to comment.