Benjamin Arhen
3 years ago
114 changed files with 342 additions and 13672 deletions
@ -0,0 +1,19 @@ |
|||||
|
<component name="libraryTable"> |
||||
|
<library name="Dart SDK"> |
||||
|
<CLASSES> |
||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/async" /> |
||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/collection" /> |
||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/convert" /> |
||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/core" /> |
||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/developer" /> |
||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/html" /> |
||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/io" /> |
||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/isolate" /> |
||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/math" /> |
||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/mirrors" /> |
||||
|
<root url="file://D:\flutter/bin/cache/dart-sdk/lib/typed_data" /> |
||||
|
</CLASSES> |
||||
|
<JAVADOC /> |
||||
|
<SOURCES /> |
||||
|
</library> |
||||
|
</component> |
@ -0,0 +1,15 @@ |
|||||
|
<component name="libraryTable"> |
||||
|
<library name="KotlinJavaRuntime"> |
||||
|
<CLASSES> |
||||
|
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib.jar!/" /> |
||||
|
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar!/" /> |
||||
|
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test.jar!/" /> |
||||
|
</CLASSES> |
||||
|
<JAVADOC /> |
||||
|
<SOURCES> |
||||
|
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-stdlib-sources.jar!/" /> |
||||
|
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-reflect-sources.jar!/" /> |
||||
|
<root url="jar://$KOTLIN_BUNDLED$/lib/kotlin-test-sources.jar!/" /> |
||||
|
</SOURCES> |
||||
|
</library> |
||||
|
</component> |
@ -0,0 +1,9 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project version="4"> |
||||
|
<component name="ProjectModuleManager"> |
||||
|
<modules> |
||||
|
<module fileurl="file://$PROJECT_DIR$/teso.iml" filepath="$PROJECT_DIR$/teso.iml" /> |
||||
|
<module fileurl="file://$PROJECT_DIR$/android/teso_android.iml" filepath="$PROJECT_DIR$/android/teso_android.iml" /> |
||||
|
</modules> |
||||
|
</component> |
||||
|
</project> |
@ -0,0 +1,6 @@ |
|||||
|
<component name="ProjectRunConfigurationManager"> |
||||
|
<configuration default="false" name="main.dart" type="FlutterRunConfigurationType" factoryName="Flutter"> |
||||
|
<option name="filePath" value="$PROJECT_DIR$/lib/main.dart" /> |
||||
|
<method /> |
||||
|
</configuration> |
||||
|
</component> |
@ -0,0 +1,36 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project version="4"> |
||||
|
<component name="FileEditorManager"> |
||||
|
<leaf> |
||||
|
<file leaf-file-name="main.dart" pinned="false" current-in-tab="true"> |
||||
|
<entry file="file://$PROJECT_DIR$/lib/main.dart"> |
||||
|
<provider selected="true" editor-type-id="text-editor"> |
||||
|
<state relative-caret-position="0"> |
||||
|
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" /> |
||||
|
</state> |
||||
|
</provider> |
||||
|
</entry> |
||||
|
</file> |
||||
|
</leaf> |
||||
|
</component> |
||||
|
<component name="ToolWindowManager"> |
||||
|
<editor active="true" /> |
||||
|
<layout> |
||||
|
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" /> |
||||
|
</layout> |
||||
|
</component> |
||||
|
<component name="ProjectView"> |
||||
|
<navigator currentView="ProjectPane" proportions="" version="1"> |
||||
|
</navigator> |
||||
|
<panes> |
||||
|
<pane id="ProjectPane"> |
||||
|
<option name="show-excluded-files" value="false" /> |
||||
|
</pane> |
||||
|
</panes> |
||||
|
</component> |
||||
|
<component name="PropertiesComponent"> |
||||
|
<property name="last_opened_file_path" value="$PROJECT_DIR$" /> |
||||
|
<property name="dart.analysis.tool.window.force.activate" value="true" /> |
||||
|
<property name="show.migrate.to.gradle.popup" value="false" /> |
||||
|
</component> |
||||
|
</project> |
@ -0,0 +1,10 @@ |
|||||
|
# This file tracks properties of this Flutter project. |
||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc. |
||||
|
# |
||||
|
# This file should be version controlled and should not be manually edited. |
||||
|
|
||||
|
version: |
||||
|
revision: db747aa1331bd95bc9b3874c842261ca2d302cd5 |
||||
|
channel: stable |
||||
|
|
||||
|
project_type: app |
@ -0,0 +1,29 @@ |
|||||
|
# This file configures the analyzer, which statically analyzes Dart code to |
||||
|
# check for errors, warnings, and lints. |
||||
|
# |
||||
|
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled |
||||
|
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be |
||||
|
# invoked from the command line by running `flutter analyze`. |
||||
|
|
||||
|
# The following line activates a set of recommended lints for Flutter apps, |
||||
|
# packages, and plugins designed to encourage good coding practices. |
||||
|
#include: package:flutter_lints/flutter.yaml |
||||
|
|
||||
|
linter: |
||||
|
# The lint rules applied to this project can be customized in the |
||||
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml` |
||||
|
# included above or to enable additional rules. A list of all available lints |
||||
|
# and their documentation is published at |
||||
|
# https://dart-lang.github.io/linter/lints/index.html. |
||||
|
# |
||||
|
# Instead of disabling a lint rule for the entire project in the |
||||
|
# section below, it can also be suppressed for a single line of code |
||||
|
# or a specific dart file by using the `// ignore: name_of_lint` and |
||||
|
# `// ignore_for_file: name_of_lint` syntax on the line or in the file |
||||
|
# producing the lint. |
||||
|
rules: |
||||
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule |
||||
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule |
||||
|
|
||||
|
# Additional information about this file can be found at |
||||
|
# https://dart.dev/guides/language/analysis-options |
@ -1,31 +0,0 @@ |
|||||
import 'package:flutter_upchunk/flutter_upchunk.dart'; |
|
||||
|
|
||||
class Uploading { |
|
||||
String id; |
|
||||
String title; |
|
||||
String path; |
|
||||
String aspect; |
|
||||
String thumbnail; |
|
||||
UpChunk token; |
|
||||
double pending; |
|
||||
String campaignID; |
|
||||
String muxuploadID; |
|
||||
String muxuploadURL; |
|
||||
String muxassetID; |
|
||||
bool isProcessing; |
|
||||
|
|
||||
Uploading({ |
|
||||
this.id, |
|
||||
this.title, |
|
||||
this.path, |
|
||||
this.aspect, |
|
||||
this.thumbnail, |
|
||||
this.token, |
|
||||
this.pending, |
|
||||
this.campaignID, |
|
||||
this.isProcessing, |
|
||||
this.muxuploadID, |
|
||||
this.muxuploadURL, |
|
||||
this.muxassetID, |
|
||||
}); |
|
||||
} |
|
@ -1,48 +0,0 @@ |
|||||
import 'package:better_player/better_player.dart'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:teso/Classes/Firebase/Posts.dart'; |
|
||||
|
|
||||
const ASPECT_RATIO = 16 / 9; |
|
||||
|
|
||||
class VideoPlayerWidget extends StatefulWidget { |
|
||||
final BetterPlayerController controller; |
|
||||
final FBPosts ad; |
|
||||
|
|
||||
const VideoPlayerWidget({ |
|
||||
Key key, |
|
||||
@required this.controller, |
|
||||
@required this.ad, |
|
||||
}) : assert(controller != null), |
|
||||
assert(ad != null), |
|
||||
super(key: key); |
|
||||
|
|
||||
@override |
|
||||
_VideoPlayerWidgetState createState() => _VideoPlayerWidgetState(); |
|
||||
} |
|
||||
|
|
||||
class _VideoPlayerWidgetState extends State<VideoPlayerWidget> { |
|
||||
@override |
|
||||
void initState() { |
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return FittedBox( |
|
||||
clipBehavior: Clip.hardEdge, |
|
||||
child: SizedBox( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
child: BetterPlayer( |
|
||||
controller: widget.controller, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() { |
|
||||
widget.controller.dispose(); |
|
||||
super.dispose(); |
|
||||
} |
|
||||
} |
|
@ -1,104 +0,0 @@ |
|||||
import 'package:better_player/better_player.dart'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:provider/provider.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/CouponDetails.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/Post.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/ProductDetails/CouponList.dart'; |
|
||||
import 'package:teso/providers/user_provider.dart'; |
|
||||
|
|
||||
const ASPECT_RATIO = 16 / 9; |
|
||||
|
|
||||
class VideoPlayerWidget extends StatefulWidget { |
|
||||
final BetterPlayerController controller; |
|
||||
final Post ad; |
|
||||
final bool play; |
|
||||
final List<CouponDetails> details; |
|
||||
|
|
||||
const VideoPlayerWidget({ |
|
||||
Key key, |
|
||||
@required this.controller, |
|
||||
@required this.ad, |
|
||||
@required this.play, |
|
||||
this.details, |
|
||||
}) : assert(controller != null), |
|
||||
assert(ad != null), |
|
||||
super(key: key); |
|
||||
|
|
||||
@override |
|
||||
_VideoPlayerWidgetState createState() => _VideoPlayerWidgetState(); |
|
||||
} |
|
||||
|
|
||||
class _VideoPlayerWidgetState extends State<VideoPlayerWidget> { |
|
||||
bool displayed = false; |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
widget.controller.videoPlayerController.addListener(() => checkVideo()); |
|
||||
super.initState(); |
|
||||
|
|
||||
// if (widget.play) { |
|
||||
// _chewieController.play(); |
|
||||
// } |
|
||||
} |
|
||||
|
|
||||
checkVideo() async { |
|
||||
// Implement your calls inside these conditions' bodies : |
|
||||
if (widget.controller.videoPlayerController.value.position == |
|
||||
Duration(seconds: 0, minutes: 0, hours: 0)) { |
|
||||
// print('video Started'); |
|
||||
Provider.of<UserProvider>(context, listen: false).viewPost(widget.ad); |
|
||||
} |
|
||||
|
|
||||
if (widget.controller.videoPlayerController.value.position.inSeconds > |
|
||||
(widget.controller.videoPlayerController.value.duration.inSeconds) / |
|
||||
3) { |
|
||||
// print('video Ended'); |
|
||||
if (!displayed && widget.details.length > 0) { |
|
||||
setState(() { |
|
||||
displayed = true; |
|
||||
}); |
|
||||
await Navigator.of(context).push( |
|
||||
PageRouteBuilder( |
|
||||
opaque: false, |
|
||||
pageBuilder: (_, __, ___) => CouponList( |
|
||||
couponsList: widget.details, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
// _chewieController.play(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// @override |
|
||||
// void didUpdateWidget(VideoPlayerWidget oldWidget) { |
|
||||
// if (oldWidget.play != widget.play) { |
|
||||
// if (widget.play) { |
|
||||
// _chewieController.play(); |
|
||||
// } else { |
|
||||
// _chewieController.pause(); |
|
||||
// } |
|
||||
// } |
|
||||
// super.didUpdateWidget(oldWidget); |
|
||||
// } |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return FittedBox( |
|
||||
clipBehavior: Clip.hardEdge, |
|
||||
child: SizedBox( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
child: BetterPlayer( |
|
||||
controller: widget.controller, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() { |
|
||||
widget.controller.dispose(); |
|
||||
super.dispose(); |
|
||||
} |
|
||||
} |
|
@ -1 +0,0 @@ |
|||||
export 'video_player_widget.dart'; |
|
@ -1,141 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
import 'PageWidgets/Campaigns/header.dart'; |
|
||||
import 'package:teso/Classes/API Clasess/Campaign.dart'; |
|
||||
import 'PageWidgets/Campaigns/campaignTile.dart'; |
|
||||
import 'package:flutter/cupertino.dart'; |
|
||||
import 'package:shared_preferences/shared_preferences.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
import 'package:http/http.dart' as http; |
|
||||
import 'dart:convert'; |
|
||||
import 'dart:async'; |
|
||||
|
|
||||
class Campaigns extends StatefulWidget { |
|
||||
@override |
|
||||
_CampaignsState createState() => _CampaignsState(); |
|
||||
} |
|
||||
|
|
||||
class _CampaignsState extends State<Campaigns> { |
|
||||
TextEditingController searchkey; |
|
||||
List<Campaign> campaignMain; |
|
||||
List<Campaign> campaign; |
|
||||
var _future; |
|
||||
|
|
||||
void clearText() { |
|
||||
setState(() { |
|
||||
searchkey.clear(); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
Future<List<Campaign>> getCampaigns() async { |
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance(); |
|
||||
|
|
||||
Map<String, String> requestHeaders = { |
|
||||
'Content-type': 'application/json', |
|
||||
'Authorization': prefs.getString('tokensTeso') |
|
||||
}; |
|
||||
|
|
||||
var register2 = serverLocation + 'adverts/businesscampaigns'; |
|
||||
var client1 = await http.post(Uri.parse(register2), |
|
||||
body: json.encode(searchkey.text), headers: requestHeaders); |
|
||||
|
|
||||
if (client1.statusCode == 200) { |
|
||||
var details = jsonDecode(client1.body); |
|
||||
if (mounted) |
|
||||
setState(() { |
|
||||
campaign = List<Campaign>.from( |
|
||||
details.map((model) => Campaign.fromJSON(model)).toList()); |
|
||||
}); |
|
||||
if (campaignMain == null) { |
|
||||
setState(() { |
|
||||
campaignMain = campaign; |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
return campaign; |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
super.initState(); |
|
||||
searchkey = new TextEditingController(); |
|
||||
_future = getCampaigns(); |
|
||||
searchkey.addListener(() async { |
|
||||
if (searchkey.text.isNotEmpty) { |
|
||||
getCampaigns(); |
|
||||
} else { |
|
||||
setState(() { |
|
||||
campaign = campaignMain; |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Scaffold( |
|
||||
appBar: AppBar( |
|
||||
backgroundColor: Colors.transparent, |
|
||||
automaticallyImplyLeading: true, |
|
||||
title: Text("Join a Campaign"), |
|
||||
centerTitle: true, |
|
||||
), |
|
||||
body: Container( |
|
||||
// padding: EdgeInsets.only( |
|
||||
// left: 10, |
|
||||
// right: 10, |
|
||||
// ), |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
buildCampaignHead(context, searchkey, clearText), |
|
||||
SingleChildScrollView( |
|
||||
scrollDirection: Axis.vertical, |
|
||||
child: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
// height: MediaQuery.of(context).size.height, |
|
||||
child: FutureBuilder( |
|
||||
future: _future, |
|
||||
builder: (context, snapshot) { |
|
||||
if (snapshot.data == null && |
|
||||
snapshot.connectionState == ConnectionState.waiting) { |
|
||||
return Container( |
|
||||
child: Center( |
|
||||
child: CupertinoActivityIndicator( |
|
||||
animating: true, |
|
||||
radius: 15, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} else if (snapshot.data == null && |
|
||||
snapshot.connectionState == ConnectionState.done) { |
|
||||
return Container( |
|
||||
height: MediaQuery.of(context).size.width, |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: Center( |
|
||||
child: Text( |
|
||||
"Sorry there are no open campaigns at the moment"), |
|
||||
), |
|
||||
); |
|
||||
} else { |
|
||||
return ListView.builder( |
|
||||
primary: true, |
|
||||
scrollDirection: Axis.vertical, |
|
||||
shrinkWrap: true, |
|
||||
itemCount: campaign.length, |
|
||||
itemBuilder: (context, index) { |
|
||||
return buildCampaign( |
|
||||
context, |
|
||||
campaign.elementAt(index), |
|
||||
); |
|
||||
}, |
|
||||
); |
|
||||
} |
|
||||
}, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,142 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:teso/Classes/API Clasess/Campaign.dart'; |
|
||||
import 'package:teso/providers/pageAnimations.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
import 'package:jiffy/jiffy.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/Campaign/AuditionPage.dart'; |
|
||||
|
|
||||
buildCampaign(BuildContext context, Campaign campaignItem) { |
|
||||
return Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
//height: 120, |
|
||||
// padding: EdgeInsets.only( |
|
||||
// left: 10, |
|
||||
// right: 10, |
|
||||
// ), |
|
||||
child: Material( |
|
||||
elevation: 2.5, |
|
||||
child: SingleChildScrollView( |
|
||||
scrollDirection: Axis.horizontal, |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: 90, |
|
||||
height: 95, |
|
||||
decoration: BoxDecoration( |
|
||||
border: Border.all( |
|
||||
color: Colors.grey, |
|
||||
width: 0.5, |
|
||||
), |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topRight: Radius.circular(30), |
|
||||
topLeft: Radius.circular(30), |
|
||||
bottomLeft: Radius.circular(30), |
|
||||
bottomRight: Radius.circular(30), |
|
||||
), |
|
||||
), |
|
||||
child: ClipRRect( |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topLeft: Radius.circular(30.0), |
|
||||
topRight: Radius.circular(30.0), |
|
||||
bottomLeft: Radius.circular(30), |
|
||||
bottomRight: Radius.circular(30), |
|
||||
), |
|
||||
child: Image( |
|
||||
width: MediaQuery.of(context).size.width * 0.28, |
|
||||
height: 110, |
|
||||
fit: BoxFit.fill, |
|
||||
image: NetworkImage(campaignItem.targetProduct != null |
|
||||
? productURL + campaignItem.targetProduct |
|
||||
: ""), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
width: MediaQuery.of(context).size.width - 105, |
|
||||
//height: 110, |
|
||||
padding: EdgeInsets.all(5), |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: double.infinity, |
|
||||
child: Wrap( |
|
||||
direction: Axis.horizontal, |
|
||||
children: [ |
|
||||
Text( |
|
||||
"Campaign Title : ", |
|
||||
style: TextStyle(fontWeight: FontWeight.bold), |
|
||||
), |
|
||||
Text(campaignItem.title) |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
width: double.infinity, |
|
||||
child: Wrap( |
|
||||
direction: Axis.horizontal, |
|
||||
children: [ |
|
||||
Text( |
|
||||
"Description : ", |
|
||||
style: TextStyle(fontWeight: FontWeight.bold), |
|
||||
), |
|
||||
Text( |
|
||||
campaignItem.description.length > 90 |
|
||||
? campaignItem.description.substring(0, 90) + |
|
||||
"...." |
|
||||
: campaignItem.description, |
|
||||
) |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
width: double.infinity, |
|
||||
child: Wrap( |
|
||||
direction: Axis.horizontal, |
|
||||
children: [ |
|
||||
Text( |
|
||||
"Start Date : ", |
|
||||
style: TextStyle(fontWeight: FontWeight.bold), |
|
||||
), |
|
||||
Text(Jiffy(campaignItem.startDate).yMMMMd), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
width: double.infinity, |
|
||||
child: Align( |
|
||||
alignment: Alignment.centerRight, |
|
||||
child: Container( |
|
||||
width: 90, |
|
||||
child: ElevatedButton( |
|
||||
style: ElevatedButton.styleFrom( |
|
||||
shape: RoundedRectangleBorder( |
|
||||
borderRadius: BorderRadius.all( |
|
||||
Radius.circular(20.0), |
|
||||
), |
|
||||
), |
|
||||
primary: accentMain, |
|
||||
), |
|
||||
onPressed: () => Navigator.push( |
|
||||
context, |
|
||||
PageTransition( |
|
||||
child: Audition( |
|
||||
campaign: campaignItem, |
|
||||
), |
|
||||
type: PageTransitionType.rightToLeft, |
|
||||
), |
|
||||
), |
|
||||
child: Text("Join"), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
@ -1,45 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
buildCampaignHead( |
|
||||
BuildContext context, TextEditingController searchkey, Function filter) { |
|
||||
return Container( |
|
||||
height: 60.0, |
|
||||
//margin: EdgeInsets.all(10.0), |
|
||||
padding: EdgeInsets.all(10.0), |
|
||||
child: Material( |
|
||||
elevation: 4.0, |
|
||||
borderRadius: BorderRadius.circular(12.0), |
|
||||
shadowColor: Theme.of(context).backgroundColor, |
|
||||
child: Row( |
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch, |
|
||||
children: [ |
|
||||
//buildSmartSearch(context), |
|
||||
new Expanded( |
|
||||
child: InkWell( |
|
||||
onTap: () => print("Searching"), |
|
||||
child: TextField( |
|
||||
autofocus: false, |
|
||||
enabled: true, |
|
||||
textAlign: TextAlign.start, |
|
||||
controller: searchkey, |
|
||||
onChanged: (String v) => filter(v), |
|
||||
style: TextStyle( |
|
||||
color: Theme.of(context).primaryColorLight, |
|
||||
), |
|
||||
decoration: InputDecoration( |
|
||||
border: InputBorder.none, |
|
||||
prefixIcon: Icon( |
|
||||
Icons.search, |
|
||||
color: Theme.of(context).primaryColorLight, |
|
||||
), |
|
||||
hintText: "Search", |
|
||||
hintStyle: TextStyle(color: Colors.grey), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
@ -1,137 +0,0 @@ |
|||||
import 'dart:typed_data'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
buildCommentTile(BuildContext context, bool available, Uint8List bytes, |
|
||||
TextEditingController controller) { |
|
||||
return Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: 170, |
|
||||
margin: EdgeInsets.only(bottom: 20), |
|
||||
decoration: BoxDecoration( |
|
||||
borderRadius: BorderRadius.circular(30.0), |
|
||||
), |
|
||||
child: ClipRRect( |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topLeft: Radius.circular(30.0), |
|
||||
topRight: Radius.circular(30.0), |
|
||||
bottomLeft: Radius.circular(30), |
|
||||
bottomRight: Radius.circular(30), |
|
||||
), |
|
||||
child: Material( |
|
||||
elevation: 50.0, |
|
||||
borderRadius: BorderRadius.circular(12.0), |
|
||||
child: InkWell( |
|
||||
onTap: () { |
|
||||
// Navigator.push( |
|
||||
// context, |
|
||||
// PageTransition( |
|
||||
// type: PageTransitionType.rightToLeft, |
|
||||
// child: CommentSection(), |
|
||||
// ), |
|
||||
// ); |
|
||||
}, |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
Container( |
|
||||
margin: EdgeInsets.only(top: 10), |
|
||||
height: 30, |
|
||||
width: double.infinity, |
|
||||
child: Center( |
|
||||
child: Text( |
|
||||
"Comments", |
|
||||
style: TextStyle( |
|
||||
fontSize: 16, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Divider(), |
|
||||
Container( |
|
||||
padding: EdgeInsets.symmetric(horizontal: 10), |
|
||||
width: double.infinity, |
|
||||
child: Text("Love this post ? Say something!")), |
|
||||
GestureDetector( |
|
||||
onTap: () => print("hello"), |
|
||||
child: Container( |
|
||||
padding: EdgeInsets.symmetric( |
|
||||
vertical: 08, |
|
||||
horizontal: 6, |
|
||||
), |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: Row( |
|
||||
children: [ |
|
||||
Container( |
|
||||
height: 45.0, |
|
||||
width: 50.0, |
|
||||
margin: EdgeInsets.only(right: 8), |
|
||||
decoration: new BoxDecoration( |
|
||||
shape: BoxShape.circle, |
|
||||
color: Colors.grey, |
|
||||
), |
|
||||
child: !available |
|
||||
? Center( |
|
||||
child: Text("B"), |
|
||||
) |
|
||||
: Image( |
|
||||
fit: BoxFit.fill, |
|
||||
image: MemoryImage(bytes), |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
width: MediaQuery.of(context).size.width * 0.55, |
|
||||
height: 50, |
|
||||
child: TextField( |
|
||||
maxLines: 2, |
|
||||
autofocus: false, |
|
||||
enabled: true, |
|
||||
textAlign: TextAlign.start, |
|
||||
controller: controller, |
|
||||
style: TextStyle( |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
decoration: InputDecoration( |
|
||||
border: InputBorder.none, |
|
||||
//contentPadding: EdgeInsets.only(top: 14.0), |
|
||||
hintText: "Add a comment", |
|
||||
hintStyle: TextStyle(color: Colors.grey), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
GestureDetector( |
|
||||
onTap: () { |
|
||||
print("send comment"); |
|
||||
}, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.all(20), |
|
||||
height: 30, |
|
||||
width: 30, |
|
||||
decoration: BoxDecoration( |
|
||||
borderRadius: BorderRadius.circular(30.0), |
|
||||
color: Color.fromRGBO(0, 0, 0, 0.4), |
|
||||
), |
|
||||
child: ClipRRect( |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topLeft: Radius.circular(30.0), |
|
||||
topRight: Radius.circular(30.0), |
|
||||
bottomLeft: Radius.circular(30), |
|
||||
bottomRight: Radius.circular(30), |
|
||||
), |
|
||||
child: Align( |
|
||||
alignment: Alignment.center, |
|
||||
child: Icon( |
|
||||
Icons.send, |
|
||||
)), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
@ -1,40 +0,0 @@ |
|||||
import 'package:cached_network_image/cached_network_image.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/Post.dart'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:teso/Classes/TesoUser.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/Posts/SpecialPosts.dart'; |
|
||||
import 'package:teso/providers/pageAnimations.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
|
|
||||
buildPosted(BuildContext context, Post post, double height, TesoUser user, |
|
||||
bool addable) { |
|
||||
return Container( |
|
||||
margin: EdgeInsets.all(3), |
|
||||
width: MediaQuery.of(context).size.width * 0.5, |
|
||||
height: MediaQuery.of(context).size.width * height, |
|
||||
color: Colors.black, |
|
||||
child: GestureDetector( |
|
||||
onTap: () { |
|
||||
Navigator.push( |
|
||||
context, |
|
||||
PageTransition( |
|
||||
child: new ViewPost( |
|
||||
postedAd: post, |
|
||||
play: true, |
|
||||
), |
|
||||
type: PageTransitionType.fade)); |
|
||||
}, |
|
||||
child: CachedNetworkImage( |
|
||||
imageUrl: tesoPostThumb(post.playbackID), |
|
||||
imageBuilder: (context, imageProvider) => FadeInImage( |
|
||||
width: double.infinity, |
|
||||
fit: BoxFit.fill, |
|
||||
image: imageProvider, |
|
||||
placeholder: AssetImage("assets/images/blank.jpg"), |
|
||||
), |
|
||||
errorWidget: (context, url, error) => |
|
||||
Image.asset("assets/images/blank.jpg"), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
@ -1,56 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/Post.dart'; |
|
||||
import 'package:teso/Classes/TesoUser.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
import 'package:time_elapsed/time_elapsed.dart'; |
|
||||
|
|
||||
buildPostTile3P(BuildContext context, TesoUser user, Post postedAd) { |
|
||||
return ListTile( |
|
||||
leading: Container( |
|
||||
width: 40, |
|
||||
height: 40, |
|
||||
decoration: BoxDecoration( |
|
||||
shape: BoxShape.circle, |
|
||||
border: Border.all( |
|
||||
color: Colors.black, |
|
||||
width: 1, |
|
||||
)), |
|
||||
child: ClipRRect( |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topLeft: Radius.circular(90.0), |
|
||||
topRight: Radius.circular(90.0), |
|
||||
bottomLeft: Radius.circular(90), |
|
||||
bottomRight: Radius.circular(90), |
|
||||
), |
|
||||
child: FadeInImage( |
|
||||
height: 90, |
|
||||
width: 90, |
|
||||
fit: BoxFit.fill, |
|
||||
image: NetworkImage( |
|
||||
serverLocation + "api/pulldp/" + postedAd.publisherID, |
|
||||
), |
|
||||
placeholder: AssetImage("assets/images/tesoDP/dp1.png"), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
title: new RichText( |
|
||||
text: new TextSpan( |
|
||||
style: new TextStyle( |
|
||||
fontSize: 14.0, |
|
||||
color: Theme.of(context).primaryColorLight, |
|
||||
), |
|
||||
children: <TextSpan>[ |
|
||||
new TextSpan( |
|
||||
text: user.username + " ", |
|
||||
style: new TextStyle(fontWeight: FontWeight.bold)), |
|
||||
new TextSpan( |
|
||||
text: postedAd.title, |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
subtitle: Text( |
|
||||
TimeElapsed.fromDateTime(postedAd.timestamp), |
|
||||
), |
|
||||
); |
|
||||
} |
|
@ -1,69 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:provider/provider.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/PostedAd.dart'; |
|
||||
import 'package:teso/providers/user_provider.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
import 'package:time_elapsed/time_elapsed.dart'; |
|
||||
|
|
||||
buildPostTile(BuildContext context, PostedAd postedAd) { |
|
||||
return Consumer<UserProvider>( |
|
||||
builder: (context, value, child) { |
|
||||
return ListTile( |
|
||||
leading: Container( |
|
||||
width: 40, |
|
||||
height: 40, |
|
||||
decoration: BoxDecoration( |
|
||||
shape: BoxShape.circle, |
|
||||
border: Border.all( |
|
||||
color: Colors.black, |
|
||||
width: 1, |
|
||||
)), |
|
||||
child: ClipRRect( |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topLeft: Radius.circular(90.0), |
|
||||
topRight: Radius.circular(90.0), |
|
||||
bottomLeft: Radius.circular(90), |
|
||||
bottomRight: Radius.circular(90), |
|
||||
), |
|
||||
child: value.currentUser.thumbnail_dp == null |
|
||||
? Center( |
|
||||
child: Text( |
|
||||
value.currentUser.username.characters |
|
||||
.characterAt(0) |
|
||||
.toString() |
|
||||
.toUpperCase(), |
|
||||
), |
|
||||
) |
|
||||
: FadeInImage( |
|
||||
height: 90, |
|
||||
width: 90, |
|
||||
fit: BoxFit.fill, |
|
||||
image: NetworkImage( |
|
||||
userdpURL + value.currentUser.thumbnail_dp), |
|
||||
placeholder: AssetImage("assets/images/tesoDP/dp1.png"), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
title: new RichText( |
|
||||
text: new TextSpan( |
|
||||
style: new TextStyle( |
|
||||
fontSize: 14.0, |
|
||||
color: Theme.of(context).primaryColorLight, |
|
||||
), |
|
||||
children: <TextSpan>[ |
|
||||
new TextSpan( |
|
||||
text: value.currentUser.username + " ", |
|
||||
style: new TextStyle(fontWeight: FontWeight.bold)), |
|
||||
new TextSpan( |
|
||||
text: postedAd.post.title, |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
subtitle: Text( |
|
||||
TimeElapsed.fromDateTime(postedAd.post.timestamp), |
|
||||
), |
|
||||
); |
|
||||
}, |
|
||||
); |
|
||||
} |
|
@ -1,35 +0,0 @@ |
|||||
import 'package:cached_network_image/cached_network_image.dart'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:teso/Classes/Firebase/Posts.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/Posts/UserPosts.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
|
|
||||
buildPosted(BuildContext context, FBPosts post, double height) { |
|
||||
return Container( |
|
||||
margin: EdgeInsets.all(3), |
|
||||
width: MediaQuery.of(context).size.width * 0.5, |
|
||||
height: MediaQuery.of(context).size.width * height, |
|
||||
color: Colors.black, |
|
||||
child: GestureDetector( |
|
||||
onTap: () { |
|
||||
Navigator.of(context).push(new PageRouteBuilder( |
|
||||
pageBuilder: (_, __, ___) => new UserPosts(postedAd: post), |
|
||||
)); |
|
||||
}, |
|
||||
child: CachedNetworkImage( |
|
||||
imageUrl: tesoPostThumb( |
|
||||
post.playbackID, |
|
||||
), |
|
||||
imageBuilder: (context, imageProvider) => FadeInImage( |
|
||||
width: double.infinity, |
|
||||
fit: BoxFit.fill, |
|
||||
image: imageProvider, |
|
||||
placeholder: AssetImage("assets/images/blank.jpg"), |
|
||||
), |
|
||||
errorWidget: (context, url, error) => Container( |
|
||||
color: Colors.grey[800], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
@ -1,89 +0,0 @@ |
|||||
import 'dart:convert'; |
|
||||
|
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:loading_indicator/loading_indicator.dart'; |
|
||||
import 'package:provider/provider.dart'; |
|
||||
import 'package:teso/Classes/Uploading.dart'; |
|
||||
import 'package:teso/providers/user_provider.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
|
|
||||
uploadTile(BuildContext context, Uploading pendingUpload) { |
|
||||
return Card( |
|
||||
child: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: 70, |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
|
||||
children: [ |
|
||||
Container( |
|
||||
//constraints: BoxConstraints(minHeight: 80, maxHeight: 150), |
|
||||
width: 60, |
|
||||
height: 60, |
|
||||
child: pendingUpload != null && pendingUpload.thumbnail != null |
|
||||
? Image( |
|
||||
width: double.infinity, |
|
||||
height: double.infinity, |
|
||||
fit: BoxFit.cover, |
|
||||
image: MemoryImage(base64Decode(pendingUpload.thumbnail)), |
|
||||
gaplessPlayback: true, |
|
||||
) |
|
||||
: Image( |
|
||||
width: double.infinity, |
|
||||
height: double.infinity, |
|
||||
fit: BoxFit.cover, |
|
||||
image: AssetImage( |
|
||||
"assets/images/blank.jpg", |
|
||||
), |
|
||||
gaplessPlayback: true, |
|
||||
), |
|
||||
), |
|
||||
pendingUpload.isProcessing |
|
||||
? new Wrap( |
|
||||
direction: Axis.vertical, |
|
||||
children: [ |
|
||||
Text("Preparing post.."), |
|
||||
LoadingIndicator( |
|
||||
indicatorType: Indicator.ballPulse, |
|
||||
|
|
||||
/// Required, The loading type of the widget |
|
||||
colors: [tesoAsh, tesoBlue, tesoGold], |
|
||||
|
|
||||
/// Optional, The color collections |
|
||||
strokeWidth: 2, |
|
||||
|
|
||||
/// Optional, The stroke of the line, only applicable to widget which contains line |
|
||||
) |
|
||||
], |
|
||||
) |
|
||||
: new Wrap( |
|
||||
direction: Axis.vertical, |
|
||||
children: [ |
|
||||
Text("Processing.."), |
|
||||
Container( |
|
||||
width: MediaQuery.of(context).size.width * 0.65, |
|
||||
height: 10, |
|
||||
child: LinearProgressIndicator( |
|
||||
value: pendingUpload.pending, |
|
||||
backgroundColor: tesoAsh, |
|
||||
valueColor: new AlwaysStoppedAnimation<Color>(tesoBlue), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
pendingUpload.isProcessing |
|
||||
? Container() |
|
||||
: Container( |
|
||||
width: 40, |
|
||||
height: 40, |
|
||||
child: InkWell( |
|
||||
onTap: () => |
|
||||
Provider.of<UserProvider>(context, listen: false) |
|
||||
.cancelUpload(pendingUpload), |
|
||||
child: Icon(Icons.close), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
@ -1,212 +0,0 @@ |
|||||
// import 'package:flutter/material.dart'; |
|
||||
|
|
||||
// import 'package:teso/Classes/TextE.dart'; |
|
||||
// import 'package:teso/util/SizeConfig.dart'; |
|
||||
// import 'textstyler/src/toolbar_action.dart'; |
|
||||
// import 'textstyler/text_style_editor.dart'; |
|
||||
|
|
||||
// // ignore: must_be_immutable |
|
||||
// class TextEdit extends StatefulWidget { |
|
||||
// Textted content; |
|
||||
// TextEdit({Key key, this.content}) : super(key: key); |
|
||||
|
|
||||
// @override |
|
||||
// _TextEditState createState() => _TextEditState(); |
|
||||
// } |
|
||||
|
|
||||
// class _TextEditState extends State<TextEdit> { |
|
||||
// TextStyle textStyle; |
|
||||
// TextAlign textAlign; |
|
||||
// List<String> fonts = [ |
|
||||
// 'Billabong', |
|
||||
// 'AlexBrush', |
|
||||
// 'Allura', |
|
||||
// 'Arizonia', |
|
||||
// 'ChunkFive', |
|
||||
// 'GrandHotel', |
|
||||
// 'GreatVibes', |
|
||||
// 'Lobster', |
|
||||
// 'OpenSans', |
|
||||
// 'OstrichSans', |
|
||||
// 'Oswald', |
|
||||
// 'Pacifico', |
|
||||
// 'Quicksand', |
|
||||
// 'Roboto', |
|
||||
// 'SEASRN', |
|
||||
// 'Windsong', |
|
||||
// ]; |
|
||||
// List<Color> paletteColors = [ |
|
||||
// Colors.black, |
|
||||
// Colors.white, |
|
||||
// Color(int.parse('0xffEA2027')), |
|
||||
// Color(int.parse('0xff006266')), |
|
||||
// Color(int.parse('0xff1B1464')), |
|
||||
// Color(int.parse('0xff5758BB')), |
|
||||
// Color(int.parse('0xff6F1E51')), |
|
||||
// Color(int.parse('0xffB53471')), |
|
||||
// Color(int.parse('0xffEE5A24')), |
|
||||
// Color(int.parse('0xff009432')), |
|
||||
// Color(int.parse('0xff0652DD')), |
|
||||
// Color(int.parse('0xff9980FA')), |
|
||||
// Color(int.parse('0xff833471')), |
|
||||
// Color(int.parse('0xff112CBC4')), |
|
||||
// Color(int.parse('0xffFDA7DF')), |
|
||||
// Color(int.parse('0xffED4C67')), |
|
||||
// Color(int.parse('0xffF79F1F')), |
|
||||
// Color(int.parse('0xffA3CB38')), |
|
||||
// Color(int.parse('0xff1289A7')), |
|
||||
// Color(int.parse('0xffD980FA')) |
|
||||
// ]; |
|
||||
// FocusNode _focus = new FocusNode(); |
|
||||
// TextEditingController controller; |
|
||||
// @override |
|
||||
// void initState() { |
|
||||
// controller = new TextEditingController(); |
|
||||
// textStyle = TextStyle( |
|
||||
// fontSize: 15, |
|
||||
// color: Colors.white, |
|
||||
// fontFamily: 'OpenSans', |
|
||||
// ); |
|
||||
// textAlign = TextAlign.left; |
|
||||
// _focus.addListener(_onFocusChange); |
|
||||
|
|
||||
// controller.text = widget.content.text != null ? widget.content.text : ""; |
|
||||
// textStyle = |
|
||||
// widget.content.textStyle != null ? widget.content.textStyle : null; |
|
||||
// textAlign = widget.content.textAlign != null |
|
||||
// ? widget.content.textAlign |
|
||||
// : TextAlign.center; |
|
||||
// super.initState(); |
|
||||
// } |
|
||||
|
|
||||
// @override |
|
||||
// void dispose() { |
|
||||
// _focus.removeListener(_onFocusChange); |
|
||||
// _focus.dispose(); |
|
||||
// super.dispose(); |
|
||||
// } |
|
||||
|
|
||||
// void _onFocusChange() { |
|
||||
// debugPrint("Focus: " + _focus.hasFocus.toString()); |
|
||||
// } |
|
||||
|
|
||||
// void verify() { |
|
||||
// if (_focus.hasFocus) { |
|
||||
// _focus.unfocus(); |
|
||||
// } else { |
|
||||
// Navigator.pop( |
|
||||
// context, |
|
||||
// new Textted( |
|
||||
// text: controller.text, |
|
||||
// textAlign: textAlign, |
|
||||
// textStyle: textStyle, |
|
||||
// )); |
|
||||
// } |
|
||||
// } |
|
||||
|
|
||||
// @override |
|
||||
// Widget build(BuildContext context) { |
|
||||
// SizeConfig().init(context); |
|
||||
// return Scaffold( |
|
||||
// resizeToAvoidBottomInset: false, |
|
||||
// backgroundColor: Color.fromRGBO(0, 0, 0, 0.8), |
|
||||
// appBar: AppBar( |
|
||||
// backgroundColor: Colors.transparent, |
|
||||
// leading: IconButton( |
|
||||
// onPressed: () => Navigator.pop(context, widget.content), |
|
||||
// icon: Icon( |
|
||||
// Feather.x, |
|
||||
// color: Colors.white, |
|
||||
// ), |
|
||||
// ), |
|
||||
// actions: [ |
|
||||
// IconButton( |
|
||||
// onPressed: verify, |
|
||||
// icon: Icon( |
|
||||
// AntDesign.check, |
|
||||
// color: Colors.white, |
|
||||
// ), |
|
||||
// ), |
|
||||
// ], |
|
||||
// ), |
|
||||
// body: Container( |
|
||||
// height: SizeConfig.safeBlockVertical * 40, |
|
||||
// child: Center( |
|
||||
// child: TextField( |
|
||||
// controller: controller, |
|
||||
// // enabled: false, |
|
||||
// focusNode: _focus, |
|
||||
// style: textStyle, |
|
||||
// textAlign: textAlign, |
|
||||
// // maxLines: 4, |
|
||||
// decoration: new InputDecoration( |
|
||||
// filled: true, |
|
||||
// enabledBorder: OutlineInputBorder( |
|
||||
// borderRadius: BorderRadius.all( |
|
||||
// Radius.circular(10.0), |
|
||||
// ), |
|
||||
// borderSide: BorderSide( |
|
||||
// color: Colors.grey.shade400, |
|
||||
// width: 2, |
|
||||
// ), |
|
||||
// ), |
|
||||
// focusedBorder: OutlineInputBorder( |
|
||||
// borderRadius: BorderRadius.all( |
|
||||
// Radius.circular(10.0), |
|
||||
// ), |
|
||||
// borderSide: BorderSide( |
|
||||
// color: Colors.blue.shade300, |
|
||||
// width: 0, |
|
||||
// ), |
|
||||
// ), |
|
||||
// contentPadding: EdgeInsets.all(15), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// extendBody: false, |
|
||||
// extendBodyBehindAppBar: false, |
|
||||
// bottomSheet: Container( |
|
||||
// height: SizeConfig.safeBlockVertical * 60, |
|
||||
// child: Container( |
|
||||
// padding: EdgeInsets.all(10), |
|
||||
// decoration: BoxDecoration( |
|
||||
// color: Theme.of(context).backgroundColor, |
|
||||
// border: Border.symmetric( |
|
||||
// horizontal: BorderSide( |
|
||||
// color: Theme.of(context).backgroundColor, |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// child: Align( |
|
||||
// alignment: Alignment.topCenter, |
|
||||
// child: SingleChildScrollView( |
|
||||
// scrollDirection: Axis.vertical, |
|
||||
// child: TextStyleEditor( |
|
||||
// fonts: fonts, |
|
||||
// paletteColors: paletteColors, |
|
||||
// textStyle: textStyle, |
|
||||
// textAlign: textAlign, |
|
||||
// initialTool: EditorToolbarAction.fontFamilyTool, |
|
||||
// onTextAlignEdited: (align) { |
|
||||
// setState(() { |
|
||||
// textAlign = align; |
|
||||
// }); |
|
||||
// }, |
|
||||
// onTextStyleEdited: (style) { |
|
||||
// setState(() { |
|
||||
// textStyle = textStyle.merge(style); |
|
||||
// }); |
|
||||
// }, |
|
||||
// onCpasLockTaggle: (caps) { |
|
||||
// print(caps); |
|
||||
// }, |
|
||||
// //onToolbarActionChanged: (fu) => , |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ); |
|
||||
// } |
|
||||
// } |
|
@ -1,840 +0,0 @@ |
|||||
// import 'dart:typed_data'; |
|
||||
// import 'package:firebase_crashlytics/firebase_crashlytics.dart'; |
|
||||
// import 'package:flutter/material.dart'; |
|
||||
// import 'package:flutter/rendering.dart'; |
|
||||
// import 'package:flutter/services.dart'; |
|
||||
// import 'dart:io'; |
|
||||
// |
|
||||
// import 'package:image_gallery_saver/image_gallery_saver.dart'; |
|
||||
// import 'package:page_transition/page_transition.dart'; |
|
||||
// import 'package:share_plus/share_plus.dart'; |
|
||||
// import 'package:teso/Classes/TextE.dart'; |
|
||||
// import 'package:teso/Pages/PageWidgets/Editors/SampleThumbnail.dart'; |
|
||||
// import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Editor/TextEditor.dart'; |
|
||||
// import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trim_editor.dart'; |
|
||||
// import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trimmer.dart'; |
|
||||
// import 'package:teso/Pages/Sub_Pages/Posts/CreatePost.dart'; |
|
||||
// import 'package:teso/util/SizeConfig.dart'; |
|
||||
// import 'package:video_player/video_player.dart'; |
|
||||
// import 'package:teso/util/consts.dart'; |
|
||||
// import 'dart:async'; |
|
||||
// import 'package:path_provider/path_provider.dart'; |
|
||||
// import 'package:teso/Classes/TesoUser.dart'; |
|
||||
// import 'package:provider/provider.dart'; |
|
||||
// import 'package:teso/providers/user_provider.dart'; |
|
||||
// import 'package:flutter/cupertino.dart'; |
|
||||
// import 'package:image/image.dart' as IMG; |
|
||||
// import 'package:video_thumbnail/video_thumbnail.dart'; |
|
||||
// import 'package:teso/Classes/ColorFilters.dart'; |
|
||||
|
|
||||
// class VideoReview extends StatefulWidget { |
|
||||
// final video; |
|
||||
// final bool recorded; |
|
||||
|
|
||||
// const VideoReview({Key key, @required this.video, @required this.recorded}) |
|
||||
// : super(key: key); |
|
||||
// @override |
|
||||
// _VideoReviewState createState() => _VideoReviewState(); |
|
||||
// } |
|
||||
|
|
||||
// class _VideoReviewState extends State<VideoReview> |
|
||||
// with TickerProviderStateMixin { |
|
||||
// Trimmer _trimmer = new Trimmer(); |
|
||||
// VideoPlayerController videoController; |
|
||||
// VoidCallback videoPlayerListener; |
|
||||
// bool muted = false; |
|
||||
// String readyVideo; |
|
||||
// Color textColor = Colors.white; |
|
||||
// double _startValue = 0.15; |
|
||||
// double _endValue = 60000.0; |
|
||||
// var _future; |
|
||||
// bool _isPlaying = false; |
|
||||
// Duration _duration; |
|
||||
// Duration _position; |
|
||||
// ByteData bytes; |
|
||||
// Uint8List imageBitmap; |
|
||||
// Uint8List thumbnail; |
|
||||
// Directory tempDirectory; |
|
||||
// TesoUser user; |
|
||||
// bool processing = false; |
|
||||
// bool downloaded = false; |
|
||||
// bool processed = false; |
|
||||
// final key = new GlobalKey(); |
|
||||
// double currentOffset = 0; |
|
||||
|
|
||||
// // ScreenshotController screenshotController = ScreenshotController(); |
|
||||
// Offset offset = Offset(0, SizeConfig.safeBlockVertical * 50); |
|
||||
// var indexFilter = 0; |
|
||||
// ScrollController controller; |
|
||||
// TextStyle textStyle; |
|
||||
// TextAlign textAlign; |
|
||||
// bool showFilter = false; |
|
||||
// Textted editting = new Textted(); |
|
||||
// List<String> fonts = [ |
|
||||
// 'Billabong', |
|
||||
// 'AlexBrush', |
|
||||
// 'Allura', |
|
||||
// 'Arizonia', |
|
||||
// 'ChunkFive', |
|
||||
// 'GrandHotel', |
|
||||
// 'GreatVibes', |
|
||||
// 'Lobster', |
|
||||
// 'OpenSans', |
|
||||
// 'OstrichSans', |
|
||||
// 'Oswald', |
|
||||
// 'Pacifico', |
|
||||
// 'Quicksand', |
|
||||
// 'Roboto', |
|
||||
// 'SEASRN', |
|
||||
// 'Windsong', |
|
||||
// ]; |
|
||||
// List<Color> paletteColors = [ |
|
||||
// Colors.black, |
|
||||
// Colors.white, |
|
||||
// Color(int.parse('0xffEA2027')), |
|
||||
// Color(int.parse('0xff006266')), |
|
||||
// Color(int.parse('0xff1B1464')), |
|
||||
// Color(int.parse('0xff5758BB')), |
|
||||
// Color(int.parse('0xff6F1E51')), |
|
||||
// Color(int.parse('0xffB53471')), |
|
||||
// Color(int.parse('0xffEE5A24')), |
|
||||
// Color(int.parse('0xff009432')), |
|
||||
// Color(int.parse('0xff0652DD')), |
|
||||
// Color(int.parse('0xff9980FA')), |
|
||||
// Color(int.parse('0xff833471')), |
|
||||
// Color(int.parse('0xff112CBC4')), |
|
||||
// Color(int.parse('0xffFDA7DF')), |
|
||||
// Color(int.parse('0xffED4C67')), |
|
||||
// Color(int.parse('0xffF79F1F')), |
|
||||
// Color(int.parse('0xffA3CB38')), |
|
||||
// Color(int.parse('0xff1289A7')), |
|
||||
// Color(int.parse('0xffD980FA')) |
|
||||
// ]; |
|
||||
// List<ColorFilter> colorFilters = [ |
|
||||
// new ColorFilter(code: Color(0xFFffffff), name: "Original"), |
|
||||
// new ColorFilter(code: Color(0xFFffffff), name: "White"), |
|
||||
// new ColorFilter(code: Color(0xFF5E2612), name: "Sepia"), |
|
||||
// new ColorFilter(code: Color(0xFF8BA446), name: "Martini Olive"), |
|
||||
// new ColorFilter(code: Color(0xFFFFF8DC), name: "Cornsilk"), |
|
||||
// new ColorFilter(code: Color(0xFFCDB7B5), name: "Mistyrose"), |
|
||||
// new ColorFilter(code: Color(0xFFEEE9E9), name: "Snow"), |
|
||||
// new ColorFilter(code: Color(0xFF856363), name: "Dusty"), |
|
||||
// new ColorFilter(code: Color(0xFF8C1717), name: "Scarlet"), |
|
||||
// new ColorFilter(code: Color(0xFF615E3F), name: "Tank"), |
|
||||
// ]; |
|
||||
|
|
||||
// void _scrollListener() { |
|
||||
// setState(() { |
|
||||
// indexFilter = |
|
||||
// (controller.offset / MediaQuery.of(context).size.width).round() + 1; |
|
||||
// if (controller.offset > currentOffset) { |
|
||||
// controller.animateTo(currentOffset + MediaQuery.of(context).size.width, |
|
||||
// duration: Duration(microseconds: 2), curve: Curves.easeIn); |
|
||||
// currentOffset = controller.offset; |
|
||||
// } else { |
|
||||
// controller.animateTo(currentOffset - MediaQuery.of(context).size.width, |
|
||||
// duration: Duration(microseconds: 2), curve: Curves.easeIn); |
|
||||
// currentOffset = controller.offset; |
|
||||
// } |
|
||||
// }); |
|
||||
// print(indexFilter); |
|
||||
// } |
|
||||
|
|
||||
// Future<void> _startVideoPlayer() async { |
|
||||
// await videoController.play(); |
|
||||
// } |
|
||||
|
|
||||
// Future<void> initializeController(String fileLocation) async { |
|
||||
// videoController = VideoPlayerController.file(File(fileLocation)); |
|
||||
|
|
||||
// videoPlayerListener = () async { |
|
||||
// Timer.run(() { |
|
||||
// this.setState(() { |
|
||||
// _position = videoController.value.position; |
|
||||
// }); |
|
||||
// setState(() { |
|
||||
// _duration = Duration(milliseconds: _endValue.round()); |
|
||||
// }); |
|
||||
// if (_duration?.compareTo(_position) == 0 || |
|
||||
// _duration?.compareTo(_position) == -1) { |
|
||||
// this.setState(() { |
|
||||
// _isPlaying = false; |
|
||||
// }); |
|
||||
// videoController.pause(); |
|
||||
// videoController.seekTo(Duration(milliseconds: _startValue.round())); |
|
||||
// } else {} |
|
||||
// }); |
|
||||
// }; |
|
||||
// videoController.addListener(videoPlayerListener); |
|
||||
// await videoController.setLooping(true); |
|
||||
// await videoController.initialize(); |
|
||||
// await _trimmer.loadVideo(videoFile: File(fileLocation)); |
|
||||
// } |
|
||||
|
|
||||
// @override |
|
||||
// void initState() { |
|
||||
// // textStyle = TextStyle( |
|
||||
// // fontSize: 15, |
|
||||
// // color: Colors.white, |
|
||||
// // fontFamily: 'OpenSans', |
|
||||
// // ); |
|
||||
// controller = new ScrollController(); |
|
||||
// controller.addListener(_scrollListener); |
|
||||
// readyVideo = widget.video; |
|
||||
// if (readyVideo != null) _future = initializeController(readyVideo); |
|
||||
// rootBundle.load("assets/images/rawLogo.png").then((value) => setState(() { |
|
||||
// imageBitmap = value.buffer.asUint8List(); |
|
||||
// IMG.Image img = IMG.decodeImage(imageBitmap); |
|
||||
// IMG.Image resized = IMG.copyResize(img, width: 50, height: 60); |
|
||||
// imageBitmap = IMG.encodePng(resized); |
|
||||
// })); |
|
||||
// super.initState(); |
|
||||
// editting = |
|
||||
// new Textted(text: "", textAlign: textAlign, textStyle: textStyle); |
|
||||
// } |
|
||||
|
|
||||
// Future<Uint8List> saveTextOverlay() async { |
|
||||
// return await screenshotController.capture( |
|
||||
// pixelRatio: videoController.value.aspectRatio); |
|
||||
// } |
|
||||
|
|
||||
// @override |
|
||||
// void dispose() { |
|
||||
// videoController?.dispose(); |
|
||||
// controller.dispose(); |
|
||||
// super.dispose(); |
|
||||
// } |
|
||||
|
|
||||
// void postVideo(context) async { |
|
||||
// setState(() { |
|
||||
// processing = true; |
|
||||
// }); |
|
||||
// if (processed) { |
|
||||
// await Navigator.pushReplacement( |
|
||||
// context, |
|
||||
// PageTransition( |
|
||||
// type: PageTransitionType.leftToRight, |
|
||||
// child: CreatePost( |
|
||||
// video: readyVideo, |
|
||||
// aspectRatio: videoController.value.aspectRatio.toString(), |
|
||||
// thumbnail: this.thumbnail, |
|
||||
// ), |
|
||||
// )); |
|
||||
// } else { |
|
||||
// readyVideo = await processVideo(context, false); |
|
||||
// await Navigator.pushReplacement( |
|
||||
// context, |
|
||||
// PageTransition( |
|
||||
// type: PageTransitionType.leftToRight, |
|
||||
// child: CreatePost( |
|
||||
// video: readyVideo, |
|
||||
// aspectRatio: videoController.value.aspectRatio.toString(), |
|
||||
// thumbnail: this.thumbnail, |
|
||||
// ), |
|
||||
// )); |
|
||||
// } |
|
||||
// } |
|
||||
|
|
||||
// Future<void> downloadVideo(context) async { |
|
||||
// try { |
|
||||
// setState(() { |
|
||||
// processing = true; |
|
||||
// }); |
|
||||
// String output = await processVideo(context, true); |
|
||||
// await ImageGallerySaver.saveFile(output).catchError((error, stackTrace) { |
|
||||
// setState(() { |
|
||||
// processing = false; |
|
||||
// downloaded = false; |
|
||||
// }); |
|
||||
// }).then((value) { |
|
||||
// setState(() { |
|
||||
// processing = false; |
|
||||
// downloaded = true; |
|
||||
// }); |
|
||||
// }); |
|
||||
// } catch (e) { |
|
||||
// print(e); |
|
||||
// } |
|
||||
// } |
|
||||
|
|
||||
// Future<String> processVideo(context, bool watermark) async { |
|
||||
// user = Provider.of<UserProvider>(context, listen: false).currentUser; |
|
||||
// String location = await getTemporaryDirectory().then((value) => |
|
||||
// value.path + |
|
||||
// "/" + |
|
||||
// DateTime.now().millisecondsSinceEpoch.toString() + |
|
||||
// ".mp4"); |
|
||||
// String initial = await _trimmer.saveTrimmedVideo( |
|
||||
// applyVideoEncoding: false, |
|
||||
// startValue: _startValue, |
|
||||
// endValue: videoController.value.duration.inMilliseconds > 5900 && |
|
||||
// videoController.value.duration.inMilliseconds >= _endValue |
|
||||
// ? _endValue |
|
||||
// : double.parse( |
|
||||
// videoController.value.duration.inMilliseconds.toString()), |
|
||||
// ); |
|
||||
// this.thumbnail = await generateThumbnail(); |
|
||||
// if (widget.recorded) { |
|
||||
// try { |
|
||||
// // Uint8List textBytes; |
|
||||
// // if (editting.text.isNotEmpty) textBytes = await saveTextOverlay(); |
|
||||
// // int xposition = ScaledPosition.getWidth( |
|
||||
// // SizeConfig.safeBlockHorizontal * 100, |
|
||||
// // videoController.value.size.width, |
|
||||
// // offset.dx); |
|
||||
// // int yposition = ScaledPosition.getHeight( |
|
||||
// // SizeConfig.safeBlockVertical * 100, |
|
||||
// // videoController.value.size.height, |
|
||||
// // offset.dy); |
|
||||
// // if (watermark) { |
|
||||
// // final tapiocaBalls = [ |
|
||||
// // if (indexFilter > 1) |
|
||||
// // TapiocaBall.filterFromColor(colorFilters[indexFilter - 1].code), |
|
||||
// // TapiocaBall.imageOverlay(imageBitmap, 0, 0), |
|
||||
// // if (textBytes != null) |
|
||||
// // TapiocaBall.imageOverlay(textBytes, xposition, yposition), |
|
||||
// // ]; |
|
||||
|
|
||||
// // final cup = Cup(Content(initial), tapiocaBalls); |
|
||||
// // await cup.suckUp(location); |
|
||||
|
|
||||
// // setState(() { |
|
||||
// // processed = true; |
|
||||
// // }); |
|
||||
// // } else if (!watermark && indexFilter == 1) { |
|
||||
// // setState(() { |
|
||||
// // processed = true; |
|
||||
// // }); |
|
||||
// // final tapiocaBalls = [ |
|
||||
// // if (textBytes != null) |
|
||||
// // TapiocaBall.imageOverlay(textBytes, xposition, yposition), |
|
||||
// // ]; |
|
||||
// // final cup = Cup(Content(initial), tapiocaBalls); |
|
||||
// // await cup.suckUp(location); |
|
||||
// // } else { |
|
||||
// // final tapiocaBalls = [ |
|
||||
// // if (indexFilter != 1 && indexFilter != 0) |
|
||||
// // TapiocaBall.filterFromColor(colorFilters[indexFilter - 1].code), |
|
||||
// // if (textBytes != null) |
|
||||
// // TapiocaBall.imageOverlay(textBytes, xposition, yposition), |
|
||||
// // ]; |
|
||||
// // final cup = Cup(Content(initial), tapiocaBalls); |
|
||||
// // await cup.suckUp(location); |
|
||||
|
|
||||
// // setState(() { |
|
||||
// // processed = true; |
|
||||
// // }); |
|
||||
// // } |
|
||||
// } catch (e) { |
|
||||
// print(e); |
|
||||
// FirebaseCrashlytics.instance.recordError( |
|
||||
// e, |
|
||||
// e, |
|
||||
// reason: "Video Editor", |
|
||||
// ); |
|
||||
// setState(() { |
|
||||
// processed = true; |
|
||||
// }); |
|
||||
// } |
|
||||
// } else { |
|
||||
// location = initial; |
|
||||
// } |
|
||||
// return location; |
|
||||
// } |
|
||||
|
|
||||
// Future<Uint8List> generateThumbnail() async { |
|
||||
// try { |
|
||||
// Uint8List thumbnail; |
|
||||
|
|
||||
// thumbnail = await VideoThumbnail.thumbnailData( |
|
||||
// video: widget.video, |
|
||||
// imageFormat: ImageFormat.JPEG, |
|
||||
// maxWidth: 0, |
|
||||
// maxHeight: 0, |
|
||||
// timeMs: 100, |
|
||||
// quality: 100, |
|
||||
// ); |
|
||||
// return thumbnail; |
|
||||
// } catch (e) { |
|
||||
// print("Error :::: " + e); |
|
||||
// return null; |
|
||||
// } |
|
||||
// } |
|
||||
|
|
||||
// Future<void> shareVideo(context) async { |
|
||||
// setState(() { |
|
||||
// processing = true; |
|
||||
// }); |
|
||||
// if (readyVideo == widget.video) { |
|
||||
// readyVideo = await processVideo(context, true); |
|
||||
// Share.shareFiles([readyVideo]); |
|
||||
// } else { |
|
||||
// Share.shareFiles([readyVideo]); |
|
||||
// } |
|
||||
// setState(() { |
|
||||
// processing = false; |
|
||||
// }); |
|
||||
// } |
|
||||
|
|
||||
// void setText() async { |
|
||||
// Textted ed = await Navigator.push( |
|
||||
// context, |
|
||||
// PageRouteBuilder( |
|
||||
// opaque: false, |
|
||||
// pageBuilder: (_, __, ___) => TextEdit( |
|
||||
// content: editting, |
|
||||
// ), |
|
||||
// ), |
|
||||
// ); |
|
||||
// if (ed != null) { |
|
||||
// setState(() { |
|
||||
// editting = ed; |
|
||||
// }); |
|
||||
// } |
|
||||
// } |
|
||||
|
|
||||
// void showFilters() async { |
|
||||
// if (thumbnail == null) this.thumbnail = await generateThumbnail(); |
|
||||
// setState(() { |
|
||||
// showFilter = !showFilter; |
|
||||
// }); |
|
||||
// } |
|
||||
|
|
||||
// void setFilter(index) { |
|
||||
// setState(() { |
|
||||
// indexFilter = index; |
|
||||
// }); |
|
||||
// } |
|
||||
|
|
||||
// @override |
|
||||
// Widget build(BuildContext context) { |
|
||||
// SizeConfig().init(context); |
|
||||
// return Scaffold( |
|
||||
// body: FutureBuilder( |
|
||||
// future: _future, |
|
||||
// builder: (context, snapshot) { |
|
||||
// if (snapshot.connectionState == ConnectionState.waiting) { |
|
||||
// return Container( |
|
||||
// color: Colors.black, |
|
||||
// width: MediaQuery.of(context).size.width, |
|
||||
// height: MediaQuery.of(context).size.height, |
|
||||
// child: Center( |
|
||||
// child: CircularProgressIndicator( |
|
||||
// backgroundColor: Colors.red, |
|
||||
// ), |
|
||||
// ), |
|
||||
// ); |
|
||||
// } else { |
|
||||
// return Stack( |
|
||||
// children: [ |
|
||||
// videoContent(context), |
|
||||
// // //Filters |
|
||||
// filterWidget(context), |
|
||||
// textWidget(context), |
|
||||
// // Video trimmer |
|
||||
// trimmerWidget(context), |
|
||||
// // Pop button |
|
||||
// Align( |
|
||||
// alignment: Alignment.topLeft, |
|
||||
// child: InkWell( |
|
||||
// onTap: () => Navigator.pop(context), |
|
||||
// child: Container( |
|
||||
// margin: EdgeInsets.symmetric( |
|
||||
// horizontal: MediaQuery.of(context).size.width * 0.07, |
|
||||
// vertical: MediaQuery.of(context).size.width * 0.1, |
|
||||
// ), |
|
||||
// height: 35, |
|
||||
// width: 35, |
|
||||
// decoration: BoxDecoration( |
|
||||
// color: Color.fromRGBO(0, 0, 0, 0.4), |
|
||||
// shape: BoxShape.circle), |
|
||||
// child: Icon( |
|
||||
// EvilIcons.close, |
|
||||
// color: Colors.white, |
|
||||
// size: 20, |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// //Filter Buttons |
|
||||
// showFilter ? listFilters(context) : Container(), |
|
||||
// // Bottom buttons |
|
||||
// bottomButtons(context), |
|
||||
// Visibility( |
|
||||
// visible: processing, |
|
||||
// child: Container( |
|
||||
// width: MediaQuery.of(context).size.width, |
|
||||
// height: MediaQuery.of(context).size.height, |
|
||||
// color: Color.fromRGBO(0, 0, 0, 0.6), |
|
||||
// padding: EdgeInsets.only( |
|
||||
// top: MediaQuery.of(context).size.width * 0.7), |
|
||||
// child: Center( |
|
||||
// child: Column( |
|
||||
// children: [ |
|
||||
// Image.asset(cupertinoActivityIndicatorSmall), |
|
||||
// Text( |
|
||||
// "Processing.....", |
|
||||
// style: TextStyle( |
|
||||
// color: Colors.white, |
|
||||
// ), |
|
||||
// ), |
|
||||
// ], |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ], |
|
||||
// ); |
|
||||
// } |
|
||||
// }), |
|
||||
// ); |
|
||||
// } |
|
||||
|
|
||||
// Widget trimmerWidget(context) { |
|
||||
// return Container( |
|
||||
// margin: EdgeInsets.symmetric( |
|
||||
// horizontal: MediaQuery.of(context).size.width * 0.01, |
|
||||
// vertical: MediaQuery.of(context).size.width * 0.20, |
|
||||
// ), |
|
||||
// width: MediaQuery.of(context).size.width, |
|
||||
// child: TrimEditor( |
|
||||
// borderPaintColor: tesoGold, |
|
||||
// circlePaintColor: tesoBlue, |
|
||||
// thumbnailQuality: 100, |
|
||||
// showDuration: true, |
|
||||
// viewerHeight: 50.0, |
|
||||
// maxVideoLength: Duration(seconds: 60), |
|
||||
// viewerWidth: MediaQuery.of(context).size.width, |
|
||||
// onChangeStart: (value) { |
|
||||
// if (!mounted) { |
|
||||
// setState(() { |
|
||||
// _startValue = value; |
|
||||
// }); |
|
||||
// } else { |
|
||||
// _startValue = value; |
|
||||
// } |
|
||||
// videoController.seekTo(Duration(milliseconds: value.round())); |
|
||||
// }, |
|
||||
// onChangeEnd: (value) { |
|
||||
// if (!mounted) { |
|
||||
// setState(() { |
|
||||
// _endValue = value; |
|
||||
// }); |
|
||||
// } else { |
|
||||
// _endValue = value; |
|
||||
// } |
|
||||
// }, |
|
||||
// onChangePlaybackState: (isPlaying) { |
|
||||
// if (mounted) |
|
||||
// setState(() { |
|
||||
// _isPlaying = isPlaying; |
|
||||
// }); |
|
||||
// }, |
|
||||
// )); |
|
||||
// } |
|
||||
|
|
||||
// Widget filterWidget(context) { |
|
||||
// return GestureDetector( |
|
||||
// onTap: () { |
|
||||
// !_isPlaying ? _startVideoPlayer() : videoController.pause(); |
|
||||
// setState(() { |
|
||||
// _isPlaying = !_isPlaying; |
|
||||
// }); |
|
||||
// }, |
|
||||
// child: Container( |
|
||||
// width: MediaQuery.of(context).size.width, |
|
||||
// height: MediaQuery.of(context).size.height, |
|
||||
// color: |
|
||||
// colorFilters.elementAt(indexFilter).name.toLowerCase() == "original" |
|
||||
// ? colorFilters.elementAt(indexFilter).code.withOpacity(0) |
|
||||
// : colorFilters.elementAt(indexFilter).code.withOpacity(0.5), |
|
||||
// ), |
|
||||
// ); |
|
||||
// } |
|
||||
|
|
||||
// Widget videoContent(context) { |
|
||||
// print(videoController.value.size.width); |
|
||||
|
|
||||
// return Container( |
|
||||
// width: MediaQuery.of(context).size.width, |
|
||||
// height: MediaQuery.of(context).size.height, |
|
||||
// color: Colors.black, |
|
||||
// child: Center( |
|
||||
// child: AspectRatio( |
|
||||
// aspectRatio: videoController.value.size != null |
|
||||
// ? videoController.value.aspectRatio |
|
||||
// : 1.0, |
|
||||
// child: Stack( |
|
||||
// children: [ |
|
||||
// InkWell( |
|
||||
// onTap: () { |
|
||||
// !_isPlaying ? _startVideoPlayer() : videoController.pause(); |
|
||||
// setState(() { |
|
||||
// _isPlaying = !_isPlaying; |
|
||||
// }); |
|
||||
// }, |
|
||||
// child: VideoPlayer( |
|
||||
// videoController, |
|
||||
// ), |
|
||||
// ), |
|
||||
// Container( |
|
||||
// width: double.infinity, |
|
||||
// height: double.infinity, |
|
||||
// child: GestureDetector( |
|
||||
// child: !_isPlaying |
|
||||
// ? Icon( |
|
||||
// Ionicons.md_play_circle, |
|
||||
// size: 60, |
|
||||
// color: Colors.white, |
|
||||
// ) |
|
||||
// : Container(), |
|
||||
// onTap: () { |
|
||||
// !_isPlaying |
|
||||
// ? _startVideoPlayer() |
|
||||
// : videoController.pause(); |
|
||||
// setState(() { |
|
||||
// _isPlaying = !_isPlaying; |
|
||||
// }); |
|
||||
// }, |
|
||||
// ), |
|
||||
// ), |
|
||||
// ], |
|
||||
// )), |
|
||||
// ), |
|
||||
// ); |
|
||||
// } |
|
||||
|
|
||||
// Widget bottomButtons(context) { |
|
||||
// if (widget.recorded) { |
|
||||
// return Align( |
|
||||
// alignment: Alignment.bottomLeft, |
|
||||
// child: Container( |
|
||||
// margin: EdgeInsets.symmetric( |
|
||||
// horizontal: MediaQuery.of(context).size.width * 0.05, |
|
||||
// vertical: SizeConfig.safeBlockVertical * 2.5, |
|
||||
// ), |
|
||||
// width: MediaQuery.of(context).size.width, |
|
||||
// child: Row( |
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
||||
// mainAxisSize: MainAxisSize.min, |
|
||||
// children: [ |
|
||||
// Container( |
|
||||
// width: 55, |
|
||||
// height: 40, |
|
||||
// padding: EdgeInsets.all(5), |
|
||||
// decoration: BoxDecoration( |
|
||||
// color: Color.fromRGBO(0, 0, 0, 0.6), |
|
||||
// borderRadius: BorderRadius.only( |
|
||||
// bottomLeft: Radius.circular(30), |
|
||||
// bottomRight: Radius.circular(30), |
|
||||
// topRight: Radius.circular(30), |
|
||||
// topLeft: Radius.circular(30), |
|
||||
// ), |
|
||||
// border: Border.all(color: Colors.white, width: 0.5)), |
|
||||
// child: InkWell( |
|
||||
// onTap: () async => |
|
||||
// !downloaded ? await downloadVideo(context) : null, |
|
||||
// child: Icon( |
|
||||
// !downloaded ? Feather.download : MaterialIcons.check, |
|
||||
// color: !downloaded ? Colors.white : Colors.green, |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// // InkWell( |
|
||||
// // onTap: setText, |
|
||||
// // child: Container( |
|
||||
// // width: 50, |
|
||||
// // padding: EdgeInsets.symmetric(horizontal: 18), |
|
||||
// // decoration: BoxDecoration( |
|
||||
// // color: Color.fromRGBO(0, 0, 0, 0.6), |
|
||||
// // borderRadius: BorderRadius.only( |
|
||||
// // bottomLeft: Radius.circular(30), |
|
||||
// // bottomRight: Radius.circular(30), |
|
||||
// // topRight: Radius.circular(30), |
|
||||
// // topLeft: Radius.circular(30), |
|
||||
// // ), |
|
||||
// // border: Border.all(color: Colors.white, width: 0.5)), |
|
||||
// // child: Text( |
|
||||
// // "T", |
|
||||
// // style: TextStyle( |
|
||||
// // color: Colors.white, |
|
||||
// // fontWeight: FontWeight.bold, |
|
||||
// // fontSize: SizeConfig.safeBlockHorizontal * 8, |
|
||||
// // fontFamily: 'DeadheadScript', |
|
||||
// // ), |
|
||||
// // ), |
|
||||
// // ), |
|
||||
// // ), |
|
||||
// // Container( |
|
||||
// // width: 55, |
|
||||
// // height: 40, |
|
||||
// // padding: EdgeInsets.all(5), |
|
||||
// // decoration: BoxDecoration( |
|
||||
// // color: Color.fromRGBO(0, 0, 0, 0.6), |
|
||||
// // borderRadius: BorderRadius.only( |
|
||||
// // bottomLeft: Radius.circular(30), |
|
||||
// // bottomRight: Radius.circular(30), |
|
||||
// // topRight: Radius.circular(30), |
|
||||
// // topLeft: Radius.circular(30), |
|
||||
// // ), |
|
||||
// // border: Border.all(color: Colors.white, width: 0.5)), |
|
||||
// // child: InkWell( |
|
||||
// // onTap: showFilters, |
|
||||
// // child: Image( |
|
||||
// // image: AssetImage("assets/images/color-filters.png"), |
|
||||
// // ), |
|
||||
// // ), |
|
||||
// // ), |
|
||||
|
|
||||
// Container( |
|
||||
// width: 55, |
|
||||
// height: 40, |
|
||||
// padding: EdgeInsets.all(5), |
|
||||
// decoration: BoxDecoration( |
|
||||
// color: Color.fromRGBO(0, 0, 0, 0.6), |
|
||||
// borderRadius: BorderRadius.only( |
|
||||
// bottomLeft: Radius.circular(30), |
|
||||
// bottomRight: Radius.circular(30), |
|
||||
// topRight: Radius.circular(30), |
|
||||
// topLeft: Radius.circular(30), |
|
||||
// ), |
|
||||
// border: Border.all(color: Colors.white, width: 0.5)), |
|
||||
// child: InkWell( |
|
||||
// onTap: () async => await shareVideo(context), |
|
||||
// child: Icon( |
|
||||
// Entypo.share, |
|
||||
// color: Colors.white, |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// Container( |
|
||||
// padding: EdgeInsets.all(5), |
|
||||
// width: 100, |
|
||||
// height: 40, |
|
||||
// decoration: BoxDecoration( |
|
||||
// color: tesoGold, |
|
||||
// borderRadius: BorderRadius.only( |
|
||||
// bottomLeft: Radius.circular(30), |
|
||||
// bottomRight: Radius.circular(30), |
|
||||
// topRight: Radius.circular(30), |
|
||||
// topLeft: Radius.circular(30), |
|
||||
// ), |
|
||||
// ), |
|
||||
// child: InkWell( |
|
||||
// onTap: () => postVideo(context), |
|
||||
// child: Row( |
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
|
||||
// children: [ |
|
||||
// Text( |
|
||||
// "Post", |
|
||||
// style: TextStyle( |
|
||||
// fontWeight: FontWeight.bold, |
|
||||
// ), |
|
||||
// ), |
|
||||
// Icon( |
|
||||
// Ionicons.md_send, |
|
||||
// color: tesoBlue, |
|
||||
// ), |
|
||||
// ], |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ], |
|
||||
// ), |
|
||||
// ), |
|
||||
// ); |
|
||||
// } else { |
|
||||
// return Align( |
|
||||
// alignment: Alignment.bottomRight, |
|
||||
// child: Container( |
|
||||
// padding: EdgeInsets.all(5), |
|
||||
// width: 100, |
|
||||
// height: 40, |
|
||||
// margin: EdgeInsets.symmetric( |
|
||||
// vertical: 10, |
|
||||
// horizontal: 20, |
|
||||
// ), |
|
||||
// decoration: BoxDecoration( |
|
||||
// color: tesoGold, |
|
||||
// borderRadius: BorderRadius.only( |
|
||||
// bottomLeft: Radius.circular(30), |
|
||||
// bottomRight: Radius.circular(30), |
|
||||
// topRight: Radius.circular(30), |
|
||||
// topLeft: Radius.circular(30), |
|
||||
// ), |
|
||||
// ), |
|
||||
// child: InkWell( |
|
||||
// onTap: () => postVideo(context), |
|
||||
// child: Row( |
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
|
||||
// children: [ |
|
||||
// Text( |
|
||||
// "Post", |
|
||||
// style: TextStyle( |
|
||||
// fontWeight: FontWeight.bold, |
|
||||
// ), |
|
||||
// ), |
|
||||
// Icon( |
|
||||
// Ionicons.md_send, |
|
||||
// color: tesoBlue, |
|
||||
// ), |
|
||||
// ], |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ); |
|
||||
// } |
|
||||
// } |
|
||||
|
|
||||
// Widget listFilters(context) { |
|
||||
// return Align( |
|
||||
// alignment: Alignment.bottomLeft, |
|
||||
// child: Container( |
|
||||
// margin: EdgeInsets.symmetric( |
|
||||
// horizontal: MediaQuery.of(context).size.width * 0.05, |
|
||||
// vertical: SizeConfig.safeBlockVertical * 10, |
|
||||
// ), |
|
||||
// width: MediaQuery.of(context).size.width, |
|
||||
// height: SizeConfig.safeBlockVertical * 17, |
|
||||
// child: ListView.builder( |
|
||||
// scrollDirection: Axis.horizontal, |
|
||||
// itemCount: colorFilters.length, |
|
||||
// //controller: controller, |
|
||||
// itemBuilder: (context, index) { |
|
||||
// return InkWell( |
|
||||
// onTap: () => setFilter(index), |
|
||||
// child: buildFilterThumb( |
|
||||
// context, colorFilters[index], thumbnail)); |
|
||||
// }), |
|
||||
// ), |
|
||||
// ); |
|
||||
// } |
|
||||
|
|
||||
// Widget textWidget(context) { |
|
||||
// return Container( |
|
||||
// child: Positioned( |
|
||||
// left: offset.dx, |
|
||||
// top: offset.dy, |
|
||||
// child: GestureDetector( |
|
||||
// onTap: setText, |
|
||||
// onPanUpdate: (details) { |
|
||||
// setState(() { |
|
||||
// offset = Offset( |
|
||||
// offset.dx + details.delta.dx, offset.dy + details.delta.dy); |
|
||||
// }); |
|
||||
// }, |
|
||||
// child: Screenshot( |
|
||||
// controller: screenshotController, |
|
||||
// child: editting.text != null |
|
||||
// ? Text( |
|
||||
// editting.text, |
|
||||
// style: editting.textStyle, |
|
||||
// textAlign: editting.textAlign, |
|
||||
// ) |
|
||||
// : Container(), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ); |
|
||||
// } |
|
||||
// } |
|
@ -1,554 +0,0 @@ |
|||||
import 'dart:typed_data'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter/rendering.dart'; |
|
||||
import 'package:flutter/services.dart'; |
|
||||
import 'dart:io'; |
|
||||
|
|
||||
import 'package:image_gallery_saver/image_gallery_saver.dart'; |
|
||||
import 'package:page_transition/page_transition.dart'; |
|
||||
import 'package:share_plus/share_plus.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/file_formats.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trim_editor.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trimmer.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/Posts/CreatePost.dart'; |
|
||||
import 'package:teso/util/SizeConfig.dart'; |
|
||||
import 'package:video_player/video_player.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
import 'dart:async'; |
|
||||
import 'package:path_provider/path_provider.dart'; |
|
||||
import 'package:teso/Classes/TesoUser.dart'; |
|
||||
import 'package:provider/provider.dart'; |
|
||||
import 'package:teso/providers/user_provider.dart'; |
|
||||
import 'package:flutter/cupertino.dart'; |
|
||||
import 'package:image/image.dart' as IMG; |
|
||||
import 'package:video_thumbnail/video_thumbnail.dart'; |
|
||||
|
|
||||
class VideoReview extends StatefulWidget { |
|
||||
final video; |
|
||||
final bool recorded; |
|
||||
final double aspect; |
|
||||
|
|
||||
const VideoReview( |
|
||||
{Key key, @required this.video, @required this.recorded, this.aspect}) |
|
||||
: super(key: key); |
|
||||
@override |
|
||||
_VideoReviewState createState() => _VideoReviewState(); |
|
||||
} |
|
||||
|
|
||||
class _VideoReviewState extends State<VideoReview> |
|
||||
with TickerProviderStateMixin { |
|
||||
Trimmer _trimmer = new Trimmer(); |
|
||||
VideoPlayerController videoController; |
|
||||
VoidCallback videoPlayerListener; |
|
||||
bool muted = false; |
|
||||
String readyVideo; |
|
||||
Color textColor = Colors.white; |
|
||||
double _startValue = 0.15; |
|
||||
double _endValue = 60000.0; |
|
||||
var _future; |
|
||||
bool _isPlaying = false; |
|
||||
Duration _duration; |
|
||||
Duration _position; |
|
||||
ByteData bytes; |
|
||||
Uint8List imageBitmap; |
|
||||
Uint8List thumbnail; |
|
||||
Directory tempDirectory; |
|
||||
TesoUser user; |
|
||||
bool processing = false; |
|
||||
bool downloaded = false; |
|
||||
bool processed = false; |
|
||||
final key = new GlobalKey(); |
|
||||
double currentOffset = 0; |
|
||||
|
|
||||
Future<void> _startVideoPlayer() async { |
|
||||
await videoController.play(); |
|
||||
} |
|
||||
|
|
||||
Future<void> initializeController(String fileLocation) async { |
|
||||
videoController = VideoPlayerController.file(File(fileLocation)); |
|
||||
|
|
||||
videoPlayerListener = () async { |
|
||||
Timer.run(() { |
|
||||
this.setState(() { |
|
||||
_position = videoController.value.position; |
|
||||
}); |
|
||||
setState(() { |
|
||||
_duration = Duration(milliseconds: _endValue.round()); |
|
||||
}); |
|
||||
if (_duration?.compareTo(_position) == 0 || |
|
||||
_duration?.compareTo(_position) == -1) { |
|
||||
this.setState(() { |
|
||||
_isPlaying = false; |
|
||||
}); |
|
||||
videoController.pause(); |
|
||||
videoController.seekTo(Duration(milliseconds: _startValue.round())); |
|
||||
} else {} |
|
||||
}); |
|
||||
}; |
|
||||
videoController.addListener(videoPlayerListener); |
|
||||
await videoController.setLooping(true); |
|
||||
await videoController.initialize(); |
|
||||
await _trimmer.loadVideo(videoFile: File(fileLocation)); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
readyVideo = widget.video; |
|
||||
if (readyVideo != null) _future = initializeController(readyVideo); |
|
||||
rootBundle.load("assets/images/rawLogo.png").then((value) => setState(() { |
|
||||
imageBitmap = value.buffer.asUint8List(); |
|
||||
IMG.Image img = IMG.decodeImage(imageBitmap); |
|
||||
IMG.Image resized = IMG.copyResize(img, width: 50, height: 60); |
|
||||
imageBitmap = IMG.encodePng(resized); |
|
||||
})); |
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() { |
|
||||
super.dispose(); |
|
||||
videoController.dispose(); |
|
||||
} |
|
||||
|
|
||||
void postVideo(context) async { |
|
||||
setState(() { |
|
||||
processing = true; |
|
||||
}); |
|
||||
if (processed) { |
|
||||
await Navigator.pushReplacement( |
|
||||
context, |
|
||||
PageTransition( |
|
||||
type: PageTransitionType.leftToRight, |
|
||||
child: CreatePost( |
|
||||
video: readyVideo, |
|
||||
aspectRatio: widget.recorded |
|
||||
? "0.5625" |
|
||||
: videoController.value.aspectRatio.toString(), |
|
||||
thumbnail: this.thumbnail, |
|
||||
), |
|
||||
)); |
|
||||
} else { |
|
||||
readyVideo = await processVideo(context, false); |
|
||||
await Navigator.pushReplacement( |
|
||||
context, |
|
||||
PageTransition( |
|
||||
type: PageTransitionType.leftToRight, |
|
||||
child: CreatePost( |
|
||||
video: readyVideo, |
|
||||
aspectRatio: widget.recorded |
|
||||
? "0.5625" |
|
||||
: videoController.value.aspectRatio.toString(), |
|
||||
thumbnail: this.thumbnail, |
|
||||
), |
|
||||
)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Future<void> downloadVideo(context) async { |
|
||||
try { |
|
||||
setState(() { |
|
||||
processing = true; |
|
||||
}); |
|
||||
String output = await processVideo(context, true); |
|
||||
await ImageGallerySaver.saveFile(output).catchError((error, stackTrace) { |
|
||||
setState(() { |
|
||||
processing = false; |
|
||||
downloaded = false; |
|
||||
}); |
|
||||
}).then((value) { |
|
||||
setState(() { |
|
||||
processing = false; |
|
||||
downloaded = true; |
|
||||
}); |
|
||||
}); |
|
||||
} catch (e) { |
|
||||
print(e); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Future<String> processVideo(context, bool watermark) async { |
|
||||
user = Provider.of<UserProvider>(context, listen: false).currentUser; |
|
||||
String location = await getTemporaryDirectory().then((value) => |
|
||||
value.path + |
|
||||
"/" + |
|
||||
DateTime.now().millisecondsSinceEpoch.toString() + |
|
||||
".mp4"); |
|
||||
if (widget.recorded) { |
|
||||
String initial = await _trimmer.saveTrimmedVideo( |
|
||||
applyVideoEncoding: false, |
|
||||
ffmpegCommand: "-vf setsar=1:1 -aspect 9:16", |
|
||||
customVideoFormat: ".mp4", |
|
||||
startValue: _startValue, |
|
||||
endValue: videoController.value.duration.inMilliseconds > 5900 && |
|
||||
videoController.value.duration.inMilliseconds >= _endValue |
|
||||
? _endValue |
|
||||
: double.parse( |
|
||||
videoController.value.duration.inMilliseconds.toString()), |
|
||||
); |
|
||||
this.thumbnail = await generateThumbnail(); |
|
||||
|
|
||||
location = initial; |
|
||||
} else { |
|
||||
String initial = await _trimmer.saveTrimmedVideo( |
|
||||
startValue: _startValue, |
|
||||
endValue: videoController.value.duration.inMilliseconds > 5900 && |
|
||||
videoController.value.duration.inMilliseconds >= _endValue |
|
||||
? _endValue |
|
||||
: double.parse( |
|
||||
videoController.value.duration.inMilliseconds.toString()), |
|
||||
outputFormat: FileFormat.mp4, |
|
||||
); |
|
||||
this.thumbnail = await generateThumbnail(); |
|
||||
|
|
||||
location = initial; |
|
||||
} |
|
||||
return location; |
|
||||
} |
|
||||
|
|
||||
Future<Uint8List> generateThumbnail() async { |
|
||||
try { |
|
||||
Uint8List thumbnail; |
|
||||
|
|
||||
thumbnail = await VideoThumbnail.thumbnailData( |
|
||||
video: widget.video, |
|
||||
imageFormat: ImageFormat.JPEG, |
|
||||
maxWidth: 0, |
|
||||
maxHeight: 0, |
|
||||
timeMs: 100, |
|
||||
quality: 100, |
|
||||
); |
|
||||
return thumbnail; |
|
||||
} catch (e) { |
|
||||
print("Error :::: " + e); |
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Future<void> shareVideo(context) async { |
|
||||
setState(() { |
|
||||
processing = true; |
|
||||
}); |
|
||||
if (readyVideo == widget.video) { |
|
||||
readyVideo = await processVideo(context, true); |
|
||||
Share.shareFiles([readyVideo]); |
|
||||
} else { |
|
||||
Share.shareFiles([readyVideo]); |
|
||||
} |
|
||||
setState(() { |
|
||||
processing = false; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
SizeConfig().init(context); |
|
||||
return Scaffold( |
|
||||
body: FutureBuilder( |
|
||||
future: _future, |
|
||||
builder: (context, snapshot) { |
|
||||
if (snapshot.connectionState == ConnectionState.waiting) { |
|
||||
return Container( |
|
||||
color: Colors.black, |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
child: Center( |
|
||||
child: CircularProgressIndicator( |
|
||||
backgroundColor: Colors.red, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} else { |
|
||||
return Stack( |
|
||||
children: [ |
|
||||
videoContent(context), |
|
||||
// Video trimmer |
|
||||
trimmerWidget(context), |
|
||||
// Pop button |
|
||||
Align( |
|
||||
alignment: Alignment.topLeft, |
|
||||
child: InkWell( |
|
||||
onTap: () => Navigator.pop(context), |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: MediaQuery.of(context).size.width * 0.07, |
|
||||
vertical: MediaQuery.of(context).size.width * 0.1, |
|
||||
), |
|
||||
height: 35, |
|
||||
width: 35, |
|
||||
decoration: BoxDecoration( |
|
||||
color: Color.fromRGBO(0, 0, 0, 0.4), |
|
||||
shape: BoxShape.circle), |
|
||||
child: Icon( |
|
||||
Icons.close, |
|
||||
color: Colors.white, |
|
||||
size: 20, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
// Bottom buttons |
|
||||
bottomButtons(context), |
|
||||
Visibility( |
|
||||
visible: processing, |
|
||||
child: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
color: Color.fromRGBO(0, 0, 0, 0.6), |
|
||||
padding: EdgeInsets.only( |
|
||||
top: MediaQuery.of(context).size.width * 0.7), |
|
||||
child: Center( |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
CupertinoActivityIndicator( |
|
||||
animating: true, |
|
||||
radius: 15, |
|
||||
), |
|
||||
Text( |
|
||||
"Processing.....", |
|
||||
style: TextStyle( |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
); |
|
||||
} |
|
||||
}), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget trimmerWidget(context) { |
|
||||
return Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: MediaQuery.of(context).size.width * 0.01, |
|
||||
vertical: MediaQuery.of(context).size.width * 0.20, |
|
||||
), |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: TrimEditor( |
|
||||
borderPaintColor: tesoGold, |
|
||||
circlePaintColor: tesoBlue, |
|
||||
thumbnailQuality: 100, |
|
||||
showDuration: true, |
|
||||
viewerHeight: 50.0, |
|
||||
maxVideoLength: Duration(seconds: 60), |
|
||||
viewerWidth: MediaQuery.of(context).size.width, |
|
||||
onChangeStart: (value) { |
|
||||
if (!mounted) { |
|
||||
setState(() { |
|
||||
_startValue = value; |
|
||||
}); |
|
||||
} else { |
|
||||
_startValue = value; |
|
||||
} |
|
||||
videoController.seekTo(Duration(milliseconds: value.round())); |
|
||||
}, |
|
||||
onChangeEnd: (value) { |
|
||||
if (!mounted) { |
|
||||
setState(() { |
|
||||
_endValue = value; |
|
||||
}); |
|
||||
} else { |
|
||||
_endValue = value; |
|
||||
} |
|
||||
}, |
|
||||
onChangePlaybackState: (isPlaying) { |
|
||||
if (mounted) |
|
||||
setState(() { |
|
||||
_isPlaying = isPlaying; |
|
||||
}); |
|
||||
}, |
|
||||
)); |
|
||||
} |
|
||||
|
|
||||
Widget videoContent(context) { |
|
||||
print(videoController.value.size.width); |
|
||||
|
|
||||
return Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
color: Colors.black, |
|
||||
child: Center( |
|
||||
child: AspectRatio( |
|
||||
aspectRatio: videoController.value.size != null |
|
||||
? videoController.value.aspectRatio |
|
||||
: 1.0, |
|
||||
child: Stack( |
|
||||
children: [ |
|
||||
InkWell( |
|
||||
onTap: () { |
|
||||
!_isPlaying ? _startVideoPlayer() : videoController.pause(); |
|
||||
setState(() { |
|
||||
_isPlaying = !_isPlaying; |
|
||||
}); |
|
||||
}, |
|
||||
child: VideoPlayer( |
|
||||
videoController, |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
width: double.infinity, |
|
||||
height: double.infinity, |
|
||||
child: GestureDetector( |
|
||||
child: !_isPlaying |
|
||||
? Icon( |
|
||||
Icons.play_circle, |
|
||||
size: 60, |
|
||||
color: Colors.white, |
|
||||
) |
|
||||
: Container(), |
|
||||
onTap: () { |
|
||||
!_isPlaying |
|
||||
? _startVideoPlayer() |
|
||||
: videoController.pause(); |
|
||||
setState(() { |
|
||||
_isPlaying = !_isPlaying; |
|
||||
}); |
|
||||
}, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
)), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget bottomButtons(context) { |
|
||||
if (widget.recorded) { |
|
||||
return Align( |
|
||||
alignment: Alignment.bottomLeft, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: MediaQuery.of(context).size.width * 0.05, |
|
||||
vertical: SizeConfig.safeBlockVertical * 2.5, |
|
||||
), |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
||||
mainAxisSize: MainAxisSize.min, |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: 55, |
|
||||
height: 40, |
|
||||
padding: EdgeInsets.all(5), |
|
||||
decoration: BoxDecoration( |
|
||||
color: Color.fromRGBO(0, 0, 0, 0.6), |
|
||||
borderRadius: BorderRadius.only( |
|
||||
bottomLeft: Radius.circular(30), |
|
||||
bottomRight: Radius.circular(30), |
|
||||
topRight: Radius.circular(30), |
|
||||
topLeft: Radius.circular(30), |
|
||||
), |
|
||||
border: Border.all(color: Colors.white, width: 0.5)), |
|
||||
child: InkWell( |
|
||||
onTap: () async => |
|
||||
!downloaded ? await downloadVideo(context) : null, |
|
||||
child: Icon( |
|
||||
!downloaded ? Icons.download : Icons.check, |
|
||||
color: !downloaded ? Colors.white : Colors.green, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
width: 55, |
|
||||
height: 40, |
|
||||
padding: EdgeInsets.all(5), |
|
||||
decoration: BoxDecoration( |
|
||||
color: Color.fromRGBO(0, 0, 0, 0.6), |
|
||||
borderRadius: BorderRadius.only( |
|
||||
bottomLeft: Radius.circular(30), |
|
||||
bottomRight: Radius.circular(30), |
|
||||
topRight: Radius.circular(30), |
|
||||
topLeft: Radius.circular(30), |
|
||||
), |
|
||||
border: Border.all(color: Colors.white, width: 0.5)), |
|
||||
child: InkWell( |
|
||||
onTap: () async => await shareVideo(context), |
|
||||
child: Icon( |
|
||||
Icons.share, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
padding: EdgeInsets.all(5), |
|
||||
width: 100, |
|
||||
height: 40, |
|
||||
decoration: BoxDecoration( |
|
||||
color: tesoGold, |
|
||||
borderRadius: BorderRadius.only( |
|
||||
bottomLeft: Radius.circular(30), |
|
||||
bottomRight: Radius.circular(30), |
|
||||
topRight: Radius.circular(30), |
|
||||
topLeft: Radius.circular(30), |
|
||||
), |
|
||||
), |
|
||||
child: InkWell( |
|
||||
onTap: () => postVideo(context), |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
|
||||
children: [ |
|
||||
Text( |
|
||||
"Post", |
|
||||
style: TextStyle( |
|
||||
fontWeight: FontWeight.bold, |
|
||||
), |
|
||||
), |
|
||||
Icon( |
|
||||
Icons.send, |
|
||||
color: tesoBlue, |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} else { |
|
||||
return Align( |
|
||||
alignment: Alignment.bottomRight, |
|
||||
child: Container( |
|
||||
padding: EdgeInsets.all(5), |
|
||||
width: 100, |
|
||||
height: 40, |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
vertical: 10, |
|
||||
horizontal: 20, |
|
||||
), |
|
||||
decoration: BoxDecoration( |
|
||||
color: tesoGold, |
|
||||
borderRadius: BorderRadius.only( |
|
||||
bottomLeft: Radius.circular(30), |
|
||||
bottomRight: Radius.circular(30), |
|
||||
topRight: Radius.circular(30), |
|
||||
topLeft: Radius.circular(30), |
|
||||
), |
|
||||
), |
|
||||
child: InkWell( |
|
||||
onTap: () => postVideo(context), |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
|
||||
children: [ |
|
||||
Text( |
|
||||
"Post", |
|
||||
style: TextStyle( |
|
||||
fontWeight: FontWeight.bold, |
|
||||
), |
|
||||
), |
|
||||
Icon( |
|
||||
Icons.send, |
|
||||
color: tesoBlue, |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,92 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
class ColorPalette extends StatefulWidget { |
|
||||
final Color activeColor; |
|
||||
final List<Color> colors; |
|
||||
final Function(Color) onColorPicked; |
|
||||
|
|
||||
ColorPalette({ |
|
||||
this.activeColor, |
|
||||
this.onColorPicked, |
|
||||
this.colors, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
_ColorPaletteState createState() => _ColorPaletteState(); |
|
||||
} |
|
||||
|
|
||||
class _ColorPaletteState extends State<ColorPalette> { |
|
||||
Color _activeColor; |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
_activeColor = widget.activeColor ?? widget.colors[0]; |
|
||||
|
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Padding( |
|
||||
padding: EdgeInsets.all(16), |
|
||||
child: Wrap( |
|
||||
spacing: 16, |
|
||||
runSpacing: 16, |
|
||||
children: widget.colors |
|
||||
.map( |
|
||||
(color) => _ColorHolder( |
|
||||
color: color, |
|
||||
active: color == _activeColor, |
|
||||
onTap: (color) { |
|
||||
setState(() => _activeColor = color); |
|
||||
widget.onColorPicked(color); |
|
||||
}, |
|
||||
), |
|
||||
) |
|
||||
.toList(), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class _ColorHolder extends StatelessWidget { |
|
||||
final Color color; |
|
||||
final Function(Color) onTap; |
|
||||
final bool active; |
|
||||
|
|
||||
_ColorHolder({ |
|
||||
this.color, |
|
||||
this.onTap, |
|
||||
this.active = false, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Container( |
|
||||
height: 40, |
|
||||
width: 40, |
|
||||
decoration: BoxDecoration( |
|
||||
border: active |
|
||||
? Border.fromBorderSide( |
|
||||
BorderSide(color: Theme.of(context).colorScheme.onSurface)) |
|
||||
: null, |
|
||||
borderRadius: BorderRadius.circular(50), |
|
||||
), |
|
||||
child: Center( |
|
||||
child: GestureDetector( |
|
||||
onTap: () => onTap(color), |
|
||||
child: Container( |
|
||||
height: 35, |
|
||||
width: 35, |
|
||||
decoration: BoxDecoration( |
|
||||
border: Border.fromBorderSide( |
|
||||
BorderSide(color: Theme.of(context).colorScheme.onSurface)), |
|
||||
borderRadius: BorderRadius.circular(50), |
|
||||
color: color, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,30 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
class OptionButton extends StatelessWidget { |
|
||||
final bool isActive; |
|
||||
final Function() onPressed; |
|
||||
final Widget child; |
|
||||
final Size size; |
|
||||
|
|
||||
OptionButton({ |
|
||||
this.onPressed, |
|
||||
this.child, |
|
||||
this.isActive = false, |
|
||||
this.size, |
|
||||
}); |
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return RawMaterialButton( |
|
||||
constraints: BoxConstraints.tight(size ?? Size(45, 45)), |
|
||||
highlightColor: Theme.of(context).colorScheme.background, |
|
||||
splashColor: Theme.of(context).colorScheme.background, |
|
||||
fillColor: isActive ? Theme.of(context).colorScheme.background : null, |
|
||||
shape: RoundedRectangleBorder( |
|
||||
borderRadius: BorderRadius.circular(12), |
|
||||
side: BorderSide(color: Theme.of(context).colorScheme.surface), |
|
||||
), |
|
||||
child: child, |
|
||||
onPressed: onPressed, |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,87 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
import 'option_button.dart'; |
|
||||
import 'toolbar_action.dart'; |
|
||||
|
|
||||
class Toolbar extends StatefulWidget { |
|
||||
final EditorToolbarAction initialTool; |
|
||||
final Function(EditorToolbarAction) onToolSelect; |
|
||||
|
|
||||
Toolbar({ |
|
||||
this.initialTool = EditorToolbarAction.editor, |
|
||||
this.onToolSelect, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
_ToolbarState createState() => _ToolbarState(); |
|
||||
} |
|
||||
|
|
||||
class _ToolbarState extends State<Toolbar> { |
|
||||
EditorToolbarAction _selectedAction; |
|
||||
@override |
|
||||
void initState() { |
|
||||
_selectedAction = widget.initialTool; |
|
||||
|
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
|
||||
children: [ |
|
||||
// OptionButton( |
|
||||
// isActive: _selectedAction == EditorToolbarAction.editor, |
|
||||
// child: Icon(Icons.keyboard), |
|
||||
// onPressed: () { |
|
||||
// setState(() => _selectedAction = EditorToolbarAction.editor); |
|
||||
// widget.onToolSelect(EditorToolbarAction.editor); |
|
||||
// }, |
|
||||
// ), |
|
||||
OptionButton( |
|
||||
isActive: _selectedAction == EditorToolbarAction.fontFamilyTool, |
|
||||
child: Icon(Icons.title), |
|
||||
onPressed: () { |
|
||||
setState( |
|
||||
() => _selectedAction = EditorToolbarAction.fontFamilyTool); |
|
||||
widget.onToolSelect(EditorToolbarAction.fontFamilyTool); |
|
||||
}, |
|
||||
), |
|
||||
OptionButton( |
|
||||
isActive: _selectedAction == EditorToolbarAction.fontOptionTool, |
|
||||
child: Icon(Icons.strikethrough_s), |
|
||||
onPressed: () { |
|
||||
setState( |
|
||||
() => _selectedAction = EditorToolbarAction.fontOptionTool); |
|
||||
widget.onToolSelect(EditorToolbarAction.fontOptionTool); |
|
||||
}, |
|
||||
), |
|
||||
OptionButton( |
|
||||
isActive: _selectedAction == EditorToolbarAction.fontSizeTool, |
|
||||
child: Icon(Icons.format_size), |
|
||||
onPressed: () { |
|
||||
setState(() => _selectedAction = EditorToolbarAction.fontSizeTool); |
|
||||
widget.onToolSelect(EditorToolbarAction.fontSizeTool); |
|
||||
}, |
|
||||
), |
|
||||
OptionButton( |
|
||||
isActive: _selectedAction == EditorToolbarAction.fontColorTool, |
|
||||
child: Icon(Icons.format_color_text), |
|
||||
onPressed: () { |
|
||||
setState(() => _selectedAction = EditorToolbarAction.fontColorTool); |
|
||||
widget.onToolSelect(EditorToolbarAction.fontColorTool); |
|
||||
}, |
|
||||
), |
|
||||
OptionButton( |
|
||||
isActive: _selectedAction == EditorToolbarAction.backgroundColorTool, |
|
||||
child: Icon(Icons.format_color_fill), |
|
||||
onPressed: () { |
|
||||
setState(() => |
|
||||
_selectedAction = EditorToolbarAction.backgroundColorTool); |
|
||||
widget.onToolSelect(EditorToolbarAction.backgroundColorTool); |
|
||||
}, |
|
||||
), |
|
||||
], |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,8 +0,0 @@ |
|||||
enum EditorToolbarAction { |
|
||||
editor, |
|
||||
fontFamilyTool, |
|
||||
fontOptionTool, |
|
||||
fontSizeTool, |
|
||||
fontColorTool, |
|
||||
backgroundColorTool, |
|
||||
} |
|
@ -1,24 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
import '../color_palette.dart'; |
|
||||
|
|
||||
class BackgroundColorTool extends StatelessWidget { |
|
||||
final List<Color> colors; |
|
||||
final Color activeColor; |
|
||||
final Function(Color) onColorPicked; |
|
||||
|
|
||||
BackgroundColorTool({ |
|
||||
this.colors, |
|
||||
this.onColorPicked, |
|
||||
this.activeColor, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return ColorPalette( |
|
||||
activeColor: activeColor, |
|
||||
onColorPicked: onColorPicked, |
|
||||
colors: colors, |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,24 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
import '../color_palette.dart'; |
|
||||
|
|
||||
class FontColorTool extends StatelessWidget { |
|
||||
final List<Color> colors; |
|
||||
final Color activeColor; |
|
||||
final Function(Color) onColorPicked; |
|
||||
|
|
||||
FontColorTool({ |
|
||||
this.colors, |
|
||||
this.onColorPicked, |
|
||||
this.activeColor, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return ColorPalette( |
|
||||
activeColor: activeColor, |
|
||||
onColorPicked: onColorPicked, |
|
||||
colors: colors, |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,66 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
import '../option_button.dart'; |
|
||||
|
|
||||
class FontFamilyTool extends StatefulWidget { |
|
||||
final List<String> fonts; |
|
||||
final Function(String) onSelectFont; |
|
||||
final String selectedFont; |
|
||||
|
|
||||
FontFamilyTool({ |
|
||||
this.fonts, |
|
||||
this.onSelectFont, |
|
||||
this.selectedFont, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
_FontFamilyToolState createState() => _FontFamilyToolState(); |
|
||||
} |
|
||||
|
|
||||
class _FontFamilyToolState extends State<FontFamilyTool> { |
|
||||
String _selectedFont; |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
_selectedFont = widget.selectedFont ?? widget.fonts[0]; |
|
||||
|
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Wrap( |
|
||||
spacing: 12, |
|
||||
runSpacing: 12, |
|
||||
children: widget.fonts |
|
||||
.map<_FontFamily>( |
|
||||
(font) => _FontFamily( |
|
||||
font, |
|
||||
isSelected: _selectedFont == font, |
|
||||
onSelect: (selectedFont) { |
|
||||
setState(() => _selectedFont = selectedFont); |
|
||||
widget.onSelectFont(selectedFont); |
|
||||
}, |
|
||||
), |
|
||||
) |
|
||||
.toList(), |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class _FontFamily extends StatelessWidget { |
|
||||
final String font; |
|
||||
final bool isSelected; |
|
||||
final Function(String) onSelect; |
|
||||
|
|
||||
_FontFamily(this.font, {this.onSelect, this.isSelected = false}); |
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return OptionButton( |
|
||||
isActive: isSelected, |
|
||||
size: Size(90, 45), |
|
||||
onPressed: () => onSelect(font), |
|
||||
child: Center(child: Text(font, style: TextStyle(fontFamily: font))), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,123 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
class FontSizeTool extends StatelessWidget { |
|
||||
final double fontSize; |
|
||||
final double letterSpacing; |
|
||||
final double letterHeight; |
|
||||
final Function( |
|
||||
double fontSize, |
|
||||
double letterSpacing, |
|
||||
double letterHeight, |
|
||||
) onFontSizeEdited; |
|
||||
|
|
||||
FontSizeTool({ |
|
||||
this.onFontSizeEdited, |
|
||||
this.fontSize = 0, |
|
||||
this.letterSpacing = 0, |
|
||||
this.letterHeight = 0, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
double _fontSize = fontSize; |
|
||||
double _letterSpacing = letterSpacing; |
|
||||
double _letterHeight = letterHeight; |
|
||||
|
|
||||
return Padding( |
|
||||
padding: EdgeInsets.all(16), |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
_ResizeSlider( |
|
||||
value: _fontSize, |
|
||||
icon: Icons.format_size, |
|
||||
max: 45, |
|
||||
onChange: (value) { |
|
||||
_fontSize = value; |
|
||||
onFontSizeEdited(_fontSize, _letterSpacing, _letterHeight); |
|
||||
}, |
|
||||
), |
|
||||
_ResizeSlider( |
|
||||
value: _letterHeight, |
|
||||
icon: Icons.format_line_spacing, |
|
||||
max: 10, |
|
||||
onChange: (value) { |
|
||||
_letterHeight = value; |
|
||||
onFontSizeEdited(_fontSize, _letterSpacing, _letterHeight); |
|
||||
}, |
|
||||
), |
|
||||
_ResizeSlider( |
|
||||
value: _letterSpacing, |
|
||||
icon: Icons.settings_ethernet, |
|
||||
max: 10, |
|
||||
onChange: (value) { |
|
||||
_letterSpacing = value; |
|
||||
onFontSizeEdited(_fontSize, _letterSpacing, _letterHeight); |
|
||||
}, |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class _ResizeSlider extends StatefulWidget { |
|
||||
final double value; |
|
||||
final double min; |
|
||||
final double max; |
|
||||
final IconData icon; |
|
||||
final Function(double) onChange; |
|
||||
|
|
||||
_ResizeSlider({ |
|
||||
this.value, |
|
||||
this.icon, |
|
||||
this.onChange, |
|
||||
this.min = 0, |
|
||||
this.max = 100, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
_ResizeSliderState createState() => _ResizeSliderState(); |
|
||||
} |
|
||||
|
|
||||
class _ResizeSliderState extends State<_ResizeSlider> { |
|
||||
double _value; |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
_value = widget.value; |
|
||||
|
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Row( |
|
||||
children: [ |
|
||||
Icon(widget.icon), |
|
||||
Expanded( |
|
||||
child: SliderTheme( |
|
||||
data: SliderThemeData( |
|
||||
activeTrackColor: Theme.of(context).colorScheme.background, |
|
||||
inactiveTrackColor: Theme.of(context).colorScheme.background, |
|
||||
thumbColor: Theme.of(context).colorScheme.background, |
|
||||
overlayColor: |
|
||||
Theme.of(context).colorScheme.background.withOpacity(0.2), |
|
||||
trackHeight: 2, |
|
||||
), |
|
||||
child: Slider( |
|
||||
value: _value, |
|
||||
onChanged: (value) { |
|
||||
setState(() => _value = value); |
|
||||
|
|
||||
widget.onChange(value); |
|
||||
}, |
|
||||
min: widget.min, |
|
||||
max: widget.max, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Text(_value.toStringAsFixed(1)), |
|
||||
], |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,237 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
import '../option_button.dart'; |
|
||||
|
|
||||
class TextFormatTool extends StatelessWidget { |
|
||||
final Function( |
|
||||
bool bold, |
|
||||
bool italic, |
|
||||
) onTextFormatEdited; |
|
||||
final Function(bool caps) onCpasLockTaggle; |
|
||||
final Function(TextAlign textAlign) onTextAlignEdited; |
|
||||
final TextAlign textAlign; |
|
||||
final bool bold; |
|
||||
final bool italic; |
|
||||
final bool caps; |
|
||||
|
|
||||
TextFormatTool({ |
|
||||
this.onTextFormatEdited, |
|
||||
this.onTextAlignEdited, |
|
||||
this.onCpasLockTaggle, |
|
||||
this.textAlign = TextAlign.left, |
|
||||
this.bold = false, |
|
||||
this.italic = false, |
|
||||
this.caps = false, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Container( |
|
||||
margin: EdgeInsets.only(top: 36), |
|
||||
child: Column( |
|
||||
crossAxisAlignment: CrossAxisAlignment.center, |
|
||||
children: [ |
|
||||
_TextFormatEditor( |
|
||||
bold: bold, |
|
||||
italic: italic, |
|
||||
caps: caps, |
|
||||
onFormatEdited: onTextFormatEdited, |
|
||||
onCpasLockTaggle: onCpasLockTaggle, |
|
||||
), |
|
||||
SizedBox(height: 36), |
|
||||
_TextAlignEditor( |
|
||||
textAlign: textAlign, |
|
||||
onTextAlignEdited: onTextAlignEdited, |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class _TextAlignEditor extends StatefulWidget { |
|
||||
final TextAlign textAlign; |
|
||||
final Function(TextAlign textAlign) onTextAlignEdited; |
|
||||
|
|
||||
_TextAlignEditor({ |
|
||||
this.onTextAlignEdited, |
|
||||
this.textAlign = TextAlign.left, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
_TextAlignEditorState createState() => _TextAlignEditorState(); |
|
||||
} |
|
||||
|
|
||||
class _TextAlignEditorState extends State<_TextAlignEditor> { |
|
||||
TextAlign _textAlign; |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
_textAlign = widget.textAlign; |
|
||||
|
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
|
||||
children: [ |
|
||||
_TextAlignOption( |
|
||||
icon: Icons.format_align_left, |
|
||||
isActive: _textAlign == TextAlign.left, |
|
||||
onPressed: () { |
|
||||
setState(() => _textAlign = TextAlign.left); |
|
||||
widget.onTextAlignEdited(_textAlign); |
|
||||
}, |
|
||||
), |
|
||||
_TextAlignOption( |
|
||||
icon: Icons.format_align_center, |
|
||||
isActive: _textAlign == TextAlign.center, |
|
||||
onPressed: () { |
|
||||
setState(() => _textAlign = TextAlign.center); |
|
||||
widget.onTextAlignEdited(_textAlign); |
|
||||
}, |
|
||||
), |
|
||||
_TextAlignOption( |
|
||||
icon: Icons.format_align_right, |
|
||||
isActive: _textAlign == TextAlign.right, |
|
||||
onPressed: () { |
|
||||
setState(() => _textAlign = TextAlign.right); |
|
||||
widget.onTextAlignEdited(_textAlign); |
|
||||
}, |
|
||||
), |
|
||||
_TextAlignOption( |
|
||||
icon: Icons.format_align_justify, |
|
||||
isActive: _textAlign == TextAlign.justify, |
|
||||
onPressed: () { |
|
||||
setState(() => _textAlign = TextAlign.justify); |
|
||||
widget.onTextAlignEdited(_textAlign); |
|
||||
}, |
|
||||
), |
|
||||
], |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class _TextAlignOption extends StatelessWidget { |
|
||||
final IconData icon; |
|
||||
final Function() onPressed; |
|
||||
final bool isActive; |
|
||||
|
|
||||
_TextAlignOption({ |
|
||||
this.icon, |
|
||||
this.onPressed, |
|
||||
this.isActive = false, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return IconButton( |
|
||||
iconSize: 32, |
|
||||
icon: Icon(icon), |
|
||||
color: isActive |
|
||||
? Theme.of(context).iconTheme.color |
|
||||
: Theme.of(context).disabledColor, |
|
||||
onPressed: onPressed, |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class _TextFormatEditor extends StatefulWidget { |
|
||||
final Function(bool bold, bool italic) onFormatEdited; |
|
||||
final Function(bool caps) onCpasLockTaggle; |
|
||||
final bool bold; |
|
||||
final bool italic; |
|
||||
final bool caps; |
|
||||
|
|
||||
_TextFormatEditor({ |
|
||||
this.onFormatEdited, |
|
||||
this.onCpasLockTaggle, |
|
||||
this.bold = false, |
|
||||
this.italic = false, |
|
||||
this.caps = false, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
_TextFormatEditorState createState() => _TextFormatEditorState(); |
|
||||
} |
|
||||
|
|
||||
class _TextFormatEditorState extends State<_TextFormatEditor> { |
|
||||
bool _bold; |
|
||||
bool _italic; |
|
||||
bool _caps; |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
_bold = widget.bold; |
|
||||
_italic = widget.italic; |
|
||||
_caps = widget.caps; |
|
||||
|
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
|
||||
children: [ |
|
||||
_TextFormatOption( |
|
||||
title: 'BOLD', |
|
||||
icon: Icons.format_bold, |
|
||||
isActive: _bold, |
|
||||
onPressed: () { |
|
||||
setState(() => _bold = !_bold); |
|
||||
widget.onFormatEdited(_bold, _italic); |
|
||||
}, |
|
||||
), |
|
||||
_TextFormatOption( |
|
||||
title: 'ITALIC', |
|
||||
icon: Icons.format_italic, |
|
||||
isActive: _italic, |
|
||||
onPressed: () { |
|
||||
setState(() => _italic = !_italic); |
|
||||
widget.onFormatEdited(_bold, _italic); |
|
||||
}, |
|
||||
), |
|
||||
_TextFormatOption( |
|
||||
title: 'CAPS', |
|
||||
icon: Icons.keyboard_capslock, |
|
||||
isActive: _caps, |
|
||||
onPressed: () { |
|
||||
setState(() => _caps = !_caps); |
|
||||
widget.onCpasLockTaggle(_caps); |
|
||||
}, |
|
||||
), |
|
||||
], |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class _TextFormatOption extends StatelessWidget { |
|
||||
final String title; |
|
||||
final IconData icon; |
|
||||
final Function() onPressed; |
|
||||
final bool isActive; |
|
||||
|
|
||||
_TextFormatOption({ |
|
||||
this.title, |
|
||||
this.icon, |
|
||||
this.onPressed, |
|
||||
this.isActive = false, |
|
||||
}); |
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Column( |
|
||||
children: [ |
|
||||
OptionButton( |
|
||||
isActive: isActive, |
|
||||
onPressed: onPressed, |
|
||||
child: Icon(icon), |
|
||||
), |
|
||||
SizedBox(height: 12), |
|
||||
Text(title), |
|
||||
], |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,223 +0,0 @@ |
|||||
library text_style_editor; |
|
||||
|
|
||||
export 'src/toolbar_action.dart'; |
|
||||
|
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'src/toolbar_action.dart'; |
|
||||
import 'src/tools/background_color_tool.dart'; |
|
||||
import 'src/color_palette.dart'; |
|
||||
import 'src/tools/font_family_tool.dart'; |
|
||||
import 'src/tools/font_size_tool.dart'; |
|
||||
import 'src/tools/text_format_tool.dart'; |
|
||||
import 'src/toolbar.dart'; |
|
||||
|
|
||||
/// Text style editor |
|
||||
/// A flutter widget that edit text style and text alignment |
|
||||
/// |
|
||||
/// You can pass your text style or alignment to the widget |
|
||||
/// and then get the edited text style |
|
||||
class TextStyleEditor extends StatefulWidget { |
|
||||
/// Editor's font families |
|
||||
final List<String> fonts; |
|
||||
|
|
||||
/// The text style |
|
||||
final TextStyle textStyle; |
|
||||
|
|
||||
/// The text alignment |
|
||||
final TextAlign textAlign; |
|
||||
|
|
||||
/// The inithial editor tool |
|
||||
final EditorToolbarAction initialTool; |
|
||||
|
|
||||
/// Editor's palette colors |
|
||||
final List<Color> paletteColors; |
|
||||
|
|
||||
/// [onTextStyleEdited] will be called after [textStyle] prop has changed |
|
||||
final Function(TextStyle) onTextStyleEdited; |
|
||||
|
|
||||
/// [onTextAlignEdited] will be called after [textAlingment] prop has changed |
|
||||
final Function(TextAlign) onTextAlignEdited; |
|
||||
|
|
||||
/// [onCpasLockTaggle] will be called after caps lock has changed |
|
||||
final Function(bool) onCpasLockTaggle; |
|
||||
|
|
||||
/// [onToolbarActionChanged] will be called after editor's tool has changed |
|
||||
final Function(EditorToolbarAction) onToolbarActionChanged; |
|
||||
|
|
||||
/// Create a [TextStyleEditor] widget |
|
||||
/// |
|
||||
/// [fonts] list of font families that you want to use in editor. |
|
||||
/// [textStyle] initiate text style. |
|
||||
/// [textAlign] initiate text alignment. |
|
||||
/// |
|
||||
/// [onTextStyleEdited] callback will be called every time [textStyle] has changed. |
|
||||
/// [onTextAlignEdited] callback will be called every time [textAlign] has changed. |
|
||||
/// [onCpasLockTaggle] callback will be called every time caps lock has changed to off or on. |
|
||||
/// [onToolbarActionChanged] callback will be called every time editor's tool has changed. |
|
||||
TextStyleEditor({ |
|
||||
this.fonts, |
|
||||
this.textStyle, |
|
||||
this.textAlign, |
|
||||
this.paletteColors, |
|
||||
this.initialTool = EditorToolbarAction.editor, |
|
||||
this.onTextStyleEdited, |
|
||||
this.onTextAlignEdited, |
|
||||
this.onCpasLockTaggle, |
|
||||
this.onToolbarActionChanged, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
_TextStyleEditorState createState() => _TextStyleEditorState(); |
|
||||
} |
|
||||
|
|
||||
class _TextStyleEditorState extends State<TextStyleEditor> { |
|
||||
EditorToolbarAction _currentTool; |
|
||||
TextStyle _textStyle; |
|
||||
TextAlign _textAlign; |
|
||||
List<Color> _paletteColors; |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
_currentTool = widget.initialTool; |
|
||||
_textStyle = widget.textStyle; |
|
||||
_textAlign = widget.textAlign; |
|
||||
|
|
||||
// Set default palette's colors |
|
||||
_paletteColors = widget.paletteColors ?? |
|
||||
[ |
|
||||
Colors.black, |
|
||||
Colors.white, |
|
||||
Colors.red, |
|
||||
Colors.blue, |
|
||||
Colors.blueAccent, |
|
||||
Colors.brown, |
|
||||
Colors.green, |
|
||||
Colors.indigoAccent, |
|
||||
Colors.lime, |
|
||||
]; |
|
||||
|
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Column( |
|
||||
mainAxisSize: MainAxisSize.min, |
|
||||
children: [ |
|
||||
Toolbar( |
|
||||
initialTool: _currentTool, |
|
||||
onToolSelect: (action) { |
|
||||
setState(() => _currentTool = action); |
|
||||
if (widget.onToolbarActionChanged != null) { |
|
||||
widget.onToolbarActionChanged(action); |
|
||||
} |
|
||||
}, |
|
||||
), |
|
||||
Divider(), |
|
||||
Container( |
|
||||
child: SingleChildScrollView( |
|
||||
child: () { |
|
||||
// Choice tools |
|
||||
switch (_currentTool) { |
|
||||
case EditorToolbarAction.fontFamilyTool: |
|
||||
return FontFamilyTool( |
|
||||
fonts: widget.fonts, |
|
||||
selectedFont: _textStyle.fontFamily, |
|
||||
onSelectFont: (fontFamily) { |
|
||||
setState(() => _textStyle = |
|
||||
_textStyle.copyWith(fontFamily: fontFamily)); |
|
||||
|
|
||||
if (widget.onTextStyleEdited != null) { |
|
||||
widget.onTextStyleEdited(_textStyle); |
|
||||
} |
|
||||
}, |
|
||||
); |
|
||||
case EditorToolbarAction.fontOptionTool: |
|
||||
return TextFormatTool( |
|
||||
bold: _textStyle.fontWeight == FontWeight.bold, |
|
||||
italic: _textStyle.fontStyle == FontStyle.italic, |
|
||||
textAlign: _textAlign, |
|
||||
onTextFormatEdited: (bold, italic) { |
|
||||
setState(() => _textStyle = _textStyle.copyWith( |
|
||||
fontWeight: |
|
||||
bold ? FontWeight.bold : FontWeight.normal, |
|
||||
fontStyle: |
|
||||
italic ? FontStyle.italic : FontStyle.normal, |
|
||||
)); |
|
||||
|
|
||||
if (widget.onTextStyleEdited != null) { |
|
||||
widget.onTextStyleEdited(_textStyle); |
|
||||
} |
|
||||
}, |
|
||||
onTextAlignEdited: (align) { |
|
||||
setState(() => _textAlign = align); |
|
||||
|
|
||||
if (widget.onTextAlignEdited != null) { |
|
||||
widget.onTextAlignEdited(align); |
|
||||
} |
|
||||
}, |
|
||||
onCpasLockTaggle: (caps) { |
|
||||
if (widget.onCpasLockTaggle != null) { |
|
||||
widget.onCpasLockTaggle(caps); |
|
||||
} |
|
||||
}, |
|
||||
); |
|
||||
case EditorToolbarAction.fontSizeTool: |
|
||||
return FontSizeTool( |
|
||||
fontSize: _textStyle.fontSize ?? 0, |
|
||||
letterHeight: _textStyle.height ?? 1.2, |
|
||||
letterSpacing: _textStyle.letterSpacing ?? 1, |
|
||||
onFontSizeEdited: ( |
|
||||
fontSize, |
|
||||
letterSpacing, |
|
||||
letterHeight, |
|
||||
) { |
|
||||
setState(() => _textStyle = _textStyle.copyWith( |
|
||||
fontSize: fontSize, |
|
||||
height: letterHeight, |
|
||||
letterSpacing: letterSpacing, |
|
||||
)); |
|
||||
|
|
||||
if (widget.onTextStyleEdited != null) { |
|
||||
widget.onTextStyleEdited(_textStyle); |
|
||||
} |
|
||||
}, |
|
||||
); |
|
||||
case EditorToolbarAction.fontColorTool: |
|
||||
return BackgroundColorTool( |
|
||||
activeColor: _textStyle.color, |
|
||||
colors: _paletteColors, |
|
||||
onColorPicked: (color) { |
|
||||
setState( |
|
||||
() => _textStyle = _textStyle.copyWith(color: color)); |
|
||||
|
|
||||
if (widget.onTextStyleEdited != null) { |
|
||||
widget.onTextStyleEdited(_textStyle); |
|
||||
} |
|
||||
}, |
|
||||
); |
|
||||
case EditorToolbarAction.backgroundColorTool: |
|
||||
return ColorPalette( |
|
||||
activeColor: _textStyle.backgroundColor, |
|
||||
colors: _paletteColors, |
|
||||
onColorPicked: (color) { |
|
||||
setState(() => _textStyle = |
|
||||
_textStyle.copyWith(backgroundColor: color)); |
|
||||
|
|
||||
if (widget.onTextStyleEdited != null) { |
|
||||
widget.onTextStyleEdited(_textStyle); |
|
||||
} |
|
||||
}, |
|
||||
); |
|
||||
case EditorToolbarAction.editor: |
|
||||
return Container(); |
|
||||
default: |
|
||||
return Container(); |
|
||||
} |
|
||||
}(), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,499 +0,0 @@ |
|||||
import 'dart:typed_data'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:camera/camera.dart'; |
|
||||
import 'package:flutter/services.dart'; |
|
||||
|
|
||||
import 'package:image_picker/image_picker.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
import 'package:page_transition/page_transition.dart'; |
|
||||
import 'dart:async'; |
|
||||
import 'package:circular_countdown_timer/circular_countdown_timer.dart'; |
|
||||
import 'package:video_thumbnail/video_thumbnail.dart' as thumb; |
|
||||
|
|
||||
import 'Editor/VideoReview.dart'; |
|
||||
|
|
||||
// ignore: must_be_immutable |
|
||||
class RecordVideo extends StatefulWidget { |
|
||||
List<CameraDescription> connectedCameras; |
|
||||
|
|
||||
RecordVideo({Key key, this.connectedCameras}) : super(key: key); |
|
||||
@override |
|
||||
_RecordVideoState createState() => _RecordVideoState(); |
|
||||
} |
|
||||
|
|
||||
class _RecordVideoState extends State<RecordVideo> |
|
||||
with TickerProviderStateMixin { |
|
||||
CameraController _controller; |
|
||||
int selectedCamera = 0; |
|
||||
bool flash = false; |
|
||||
bool frontFlash = false; |
|
||||
bool recording = false; |
|
||||
AnimationController _recordingAnimationController; |
|
||||
XFile video; |
|
||||
String filePath; |
|
||||
int recordEnd = 60; |
|
||||
CountDownController _controllerCountDown = CountDownController(); |
|
||||
final interval = const Duration(seconds: 1); |
|
||||
final picker = ImagePicker(); |
|
||||
bool gallery = false; |
|
||||
|
|
||||
final int timerMaxSeconds = 60; |
|
||||
|
|
||||
int currentSeconds = 0; |
|
||||
|
|
||||
flipCamera() { |
|
||||
selectedCamera++; |
|
||||
if (selectedCamera < widget.connectedCameras.length) { |
|
||||
onNewCameraSelected(widget.connectedCameras.elementAt(selectedCamera)); |
|
||||
} else { |
|
||||
selectedCamera = 0; |
|
||||
onNewCameraSelected(widget.connectedCameras.elementAt(selectedCamera)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
flashCamera() { |
|
||||
try { |
|
||||
if (!flash && |
|
||||
_controller.description.lensDirection == CameraLensDirection.back) { |
|
||||
_controller.setFlashMode(FlashMode.torch); |
|
||||
setState(() { |
|
||||
flash = true; |
|
||||
frontFlash = false; |
|
||||
}); |
|
||||
} else if (!flash && |
|
||||
_controller.description.lensDirection == CameraLensDirection.front) { |
|
||||
setState(() { |
|
||||
flash = true; |
|
||||
frontFlash = true; |
|
||||
}); |
|
||||
} else if (flash && |
|
||||
_controller.description.lensDirection == CameraLensDirection.back) { |
|
||||
_controller.setFlashMode(FlashMode.off); |
|
||||
setState(() { |
|
||||
flash = false; |
|
||||
}); |
|
||||
} else { |
|
||||
setState(() { |
|
||||
flash = false; |
|
||||
frontFlash = false; |
|
||||
}); |
|
||||
} |
|
||||
} catch (e) {} |
|
||||
} |
|
||||
|
|
||||
haltRecord() async { |
|
||||
double aspect = _controller.value.aspectRatio; |
|
||||
XFile recorded = await stopVideoRecording(); |
|
||||
if (recorded != null) |
|
||||
Navigator.of(context).pushReplacement( |
|
||||
PageRouteBuilder( |
|
||||
opaque: false, |
|
||||
pageBuilder: (_, __, ___) => VideoReview( |
|
||||
video: recorded.path, |
|
||||
aspect: aspect, |
|
||||
// campaignID: widget.campaignID, |
|
||||
recorded: true, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Future<Uint8List> generateThumbnail(video) async { |
|
||||
try { |
|
||||
Uint8List thumbnail; |
|
||||
|
|
||||
thumbnail = await thumb.VideoThumbnail.thumbnailData( |
|
||||
video: video, |
|
||||
imageFormat: thumb.ImageFormat.JPEG, |
|
||||
maxWidth: 0, |
|
||||
maxHeight: 0, |
|
||||
timeMs: 1, |
|
||||
quality: 100, |
|
||||
); |
|
||||
return thumbnail; |
|
||||
} catch (e) { |
|
||||
print("Error :::: " + e); |
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
if (widget.connectedCameras == null || |
|
||||
widget.connectedCameras.length == 0) { |
|
||||
availableCameras().then((value) { |
|
||||
widget.connectedCameras = value; |
|
||||
onNewCameraSelected(widget.connectedCameras.first); |
|
||||
}); |
|
||||
} else { |
|
||||
onNewCameraSelected(widget.connectedCameras.first); |
|
||||
} |
|
||||
_recordingAnimationController = |
|
||||
new AnimationController(vsync: this, duration: Duration(seconds: 1)); |
|
||||
|
|
||||
_recordingAnimationController.repeat(reverse: true); |
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
sayCheese() async { |
|
||||
try { |
|
||||
if (flash && !frontFlash) |
|
||||
await _controller.setFlashMode(FlashMode.always); |
|
||||
await _controller.startVideoRecording(); |
|
||||
} catch (e) { |
|
||||
print(e); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Future<XFile> stopVideoRecording() async { |
|
||||
if (!_controller.value.isRecordingVideo) { |
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
try { |
|
||||
return _controller.stopVideoRecording(); |
|
||||
} on CameraException catch (e) { |
|
||||
print(e); |
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void onNewCameraSelected(CameraDescription cameraDescription) async { |
|
||||
if (_controller != null) { |
|
||||
await _controller.dispose(); |
|
||||
} |
|
||||
_controller = CameraController( |
|
||||
cameraDescription, |
|
||||
ResolutionPreset.max, |
|
||||
enableAudio: true, |
|
||||
imageFormatGroup: ImageFormatGroup.jpeg, |
|
||||
); |
|
||||
|
|
||||
// If the controller is updated then update the UI. |
|
||||
_controller.addListener(() { |
|
||||
if (mounted) setState(() {}); |
|
||||
if (_controller.value.hasError) { |
|
||||
print('Camera error ${_controller.value.errorDescription}'); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
try { |
|
||||
await _controller.initialize(); |
|
||||
_controller.lockCaptureOrientation(DeviceOrientation.portraitUp); |
|
||||
_controller.setFocusMode(FocusMode.auto); |
|
||||
} on CameraException catch (e) { |
|
||||
print(e); |
|
||||
} |
|
||||
|
|
||||
if (mounted) { |
|
||||
setState(() {}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void onHocusFocus(TapDownDetails details, BoxConstraints constraints) { |
|
||||
final offset = Offset( |
|
||||
details.localPosition.dx / constraints.maxWidth, |
|
||||
details.localPosition.dy / constraints.maxHeight, |
|
||||
); |
|
||||
_controller.setExposurePoint(offset); |
|
||||
_controller.setFocusPoint(offset); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() { |
|
||||
_controller?.dispose(); |
|
||||
_recordingAnimationController.dispose(); |
|
||||
super.dispose(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
if (_controller == null || !_controller.value.isInitialized) { |
|
||||
return Container( |
|
||||
color: Colors.black, |
|
||||
); |
|
||||
} else { |
|
||||
return Scaffold( |
|
||||
body: !gallery |
|
||||
? Stack( |
|
||||
children: [ |
|
||||
cameraWidget(context), |
|
||||
flashWidget(context), |
|
||||
cameraFlip(context), |
|
||||
cameraFlash(context), |
|
||||
recordingAnimation(context), |
|
||||
recordingCircle(context), |
|
||||
recorderWidget(context), |
|
||||
galleryPicker(context), |
|
||||
], |
|
||||
) |
|
||||
: Container(), |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
imgFromGallery() async { |
|
||||
try { |
|
||||
setState(() { |
|
||||
gallery = true; |
|
||||
}); |
|
||||
final pickedFile = await picker.pickVideo( |
|
||||
source: ImageSource.gallery, |
|
||||
maxDuration: Duration(minutes: 1), |
|
||||
); |
|
||||
|
|
||||
if (pickedFile != null) { |
|
||||
return pickedFile.path; |
|
||||
} else { |
|
||||
onNewCameraSelected(widget.connectedCameras.first); |
|
||||
print('No image selected.'); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
print(e); |
|
||||
} |
|
||||
setState(() { |
|
||||
gallery = false; |
|
||||
}); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
Widget recordingCircle(context) { |
|
||||
return Align( |
|
||||
alignment: Alignment.bottomCenter, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
vertical: MediaQuery.of(context).size.width * 0.11, |
|
||||
), |
|
||||
height: 70, |
|
||||
width: 70, |
|
||||
child: CircularCountDownTimer( |
|
||||
duration: recordEnd, |
|
||||
initialDuration: 0, |
|
||||
controller: _controllerCountDown, |
|
||||
width: MediaQuery.of(context).size.width / 2, |
|
||||
height: MediaQuery.of(context).size.height / 2, |
|
||||
ringColor: Colors.grey[300], |
|
||||
fillColor: Colors.red, |
|
||||
backgroundColor: Colors.transparent, |
|
||||
autoStart: false, |
|
||||
strokeWidth: 5.5, |
|
||||
isTimerTextShown: false, |
|
||||
strokeCap: StrokeCap.round, |
|
||||
//onStart: startTimeout, |
|
||||
onComplete: haltRecord, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget recorderWidget(context) { |
|
||||
return Align( |
|
||||
alignment: Alignment.bottomCenter, |
|
||||
child: InkWell( |
|
||||
onTap: recording |
|
||||
? haltRecord |
|
||||
: () async { |
|
||||
await _controller.startVideoRecording(); |
|
||||
setState(() { |
|
||||
_controllerCountDown.start(); |
|
||||
recording = !recording; |
|
||||
}); |
|
||||
}, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
vertical: MediaQuery.of(context).size.width * 0.11, |
|
||||
), |
|
||||
height: 70, |
|
||||
width: 70, |
|
||||
child: Icon( |
|
||||
recording ? Icons.stop : Icons.video_camera_back, |
|
||||
color: Colors.white, |
|
||||
size: 25, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget galleryPicker(context) { |
|
||||
return Align( |
|
||||
alignment: Alignment.bottomLeft, |
|
||||
child: recording |
|
||||
? Container() |
|
||||
: InkWell( |
|
||||
onTap: () async { |
|
||||
String result = await imgFromGallery(); |
|
||||
if (result != null) { |
|
||||
// _controller.dispose(); |
|
||||
Navigator.pushReplacement( |
|
||||
context, |
|
||||
PageTransition( |
|
||||
type: PageTransitionType.leftToRight, |
|
||||
child: VideoReview( |
|
||||
video: result, |
|
||||
recorded: false, |
|
||||
// campaignID: widget.campaignID, |
|
||||
), |
|
||||
)); |
|
||||
} |
|
||||
}, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: MediaQuery.of(context).size.width * 0.05, |
|
||||
vertical: MediaQuery.of(context).size.width * 0.11, |
|
||||
), |
|
||||
height: 70, |
|
||||
width: 70, |
|
||||
child: Icon( |
|
||||
Icons.photo, |
|
||||
color: Colors.white, |
|
||||
size: 27, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget recordingAnimation(context) { |
|
||||
if (!recording) |
|
||||
return Align( |
|
||||
alignment: Alignment.topLeft, |
|
||||
child: InkWell( |
|
||||
onTap: () { |
|
||||
Navigator.pop(context); |
|
||||
}, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: MediaQuery.of(context).size.width * 0.08, |
|
||||
vertical: MediaQuery.of(context).size.width * 0.11, |
|
||||
), |
|
||||
height: 35, |
|
||||
width: 35, |
|
||||
decoration: BoxDecoration( |
|
||||
//color: ColorFilterEngineLayer (0, 0, 0, 0.4), |
|
||||
shape: BoxShape.circle), |
|
||||
child: Icon( |
|
||||
Icons.arrow_back_ios, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
)); |
|
||||
else |
|
||||
return Align( |
|
||||
alignment: Alignment.topLeft, |
|
||||
child: Container( |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.center, |
|
||||
children: [ |
|
||||
Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: 5, |
|
||||
vertical: MediaQuery.of(context).size.width * 0.11, |
|
||||
), |
|
||||
padding: EdgeInsets.all(2.5), |
|
||||
height: 20, |
|
||||
width: 20, |
|
||||
decoration: BoxDecoration( |
|
||||
shape: BoxShape.circle, |
|
||||
border: Border.all( |
|
||||
color: Colors.red, |
|
||||
width: 2, |
|
||||
)), |
|
||||
child: FadeTransition( |
|
||||
opacity: _recordingAnimationController, |
|
||||
child: Container( |
|
||||
width: 20, |
|
||||
height: 20, |
|
||||
decoration: BoxDecoration( |
|
||||
shape: BoxShape.circle, |
|
||||
color: Colors.red, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget cameraFlash(context) { |
|
||||
return !recording |
|
||||
? Align( |
|
||||
alignment: Alignment.topRight, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: MediaQuery.of(context).size.width * 0.07, |
|
||||
vertical: MediaQuery.of(context).size.width * 0.25, |
|
||||
), |
|
||||
child: InkWell( |
|
||||
onTap: flashCamera, |
|
||||
child: Icon( |
|
||||
flash ? Icons.flash_on : Icons.flash_off, |
|
||||
color: flash ? tesoGold : Colors.white, |
|
||||
size: 30, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
) |
|
||||
: Container(); |
|
||||
} |
|
||||
|
|
||||
Widget cameraFlip(context) { |
|
||||
return !recording |
|
||||
? Align( |
|
||||
alignment: Alignment.topRight, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: MediaQuery.of(context).size.width * 0.06, |
|
||||
vertical: MediaQuery.of(context).size.width * 0.11, |
|
||||
), |
|
||||
child: InkWell( |
|
||||
onTap: flipCamera, |
|
||||
child: Icon( |
|
||||
Icons.cameraswitch_outlined, |
|
||||
color: Colors.white, |
|
||||
size: 40, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
) |
|
||||
: Container(); |
|
||||
} |
|
||||
|
|
||||
Widget flashWidget(context) { |
|
||||
return Visibility( |
|
||||
visible: frontFlash, |
|
||||
child: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
decoration: BoxDecoration( |
|
||||
color: Colors.white.withOpacity(0.4), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget cameraWidget(context) { |
|
||||
var camera = _controller.value; |
|
||||
final size = MediaQuery.of(context).size; |
|
||||
var scale = size.aspectRatio * camera.aspectRatio; |
|
||||
if (scale < 1) scale = 1 / scale; |
|
||||
|
|
||||
return Transform.scale( |
|
||||
scale: scale, |
|
||||
child: Center( |
|
||||
child: CameraPreview( |
|
||||
_controller, |
|
||||
child: LayoutBuilder( |
|
||||
builder: (BuildContext context, BoxConstraints constraints) { |
|
||||
return GestureDetector( |
|
||||
behavior: HitTestBehavior.opaque, |
|
||||
onTapDown: (details) => onHocusFocus(details, constraints), |
|
||||
); |
|
||||
}), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,45 +0,0 @@ |
|||||
/// The video file formats available for |
|
||||
/// generating the output trimmed video. |
|
||||
/// |
|
||||
/// The available formats are `mp4`, `mkv`, |
|
||||
/// `mov`, `flv`, `avi`, `wmv`& `gif`. |
|
||||
/// |
|
||||
/// If you define a custom `FFmpeg` command |
|
||||
/// then this will be ignored. |
|
||||
/// |
|
||||
class FileFormat { |
|
||||
const FileFormat._(this.index); |
|
||||
|
|
||||
final int index; |
|
||||
|
|
||||
static const FileFormat mp4 = FileFormat._(0); |
|
||||
static const FileFormat mkv = FileFormat._(1); |
|
||||
static const FileFormat mov = FileFormat._(2); |
|
||||
static const FileFormat flv = FileFormat._(3); |
|
||||
static const FileFormat avi = FileFormat._(4); |
|
||||
static const FileFormat wmv = FileFormat._(5); |
|
||||
static const FileFormat gif = FileFormat._(6); |
|
||||
|
|
||||
static const List<FileFormat> values = <FileFormat>[ |
|
||||
mp4, |
|
||||
mkv, |
|
||||
mov, |
|
||||
flv, |
|
||||
avi, |
|
||||
wmv, |
|
||||
gif, |
|
||||
]; |
|
||||
|
|
||||
@override |
|
||||
String toString() { |
|
||||
return const <int, String>{ |
|
||||
0: '.mp4', |
|
||||
1: '.mkv', |
|
||||
2: '.mov', |
|
||||
3: '.flv', |
|
||||
4: '.avi', |
|
||||
5: '.wmv', |
|
||||
6: '.gif', |
|
||||
}[index]; |
|
||||
} |
|
||||
} |
|
@ -1,32 +0,0 @@ |
|||||
/// Supported storage locations. |
|
||||
/// |
|
||||
/// * [temporaryDirectory] |
|
||||
/// |
|
||||
/// * [applicationDocumentsDirectory] |
|
||||
/// |
|
||||
/// * [externalStorageDirectory] |
|
||||
/// |
|
||||
class StorageDir { |
|
||||
const StorageDir._(this.index); |
|
||||
|
|
||||
final int index; |
|
||||
|
|
||||
static const StorageDir temporaryDirectory = StorageDir._(0); |
|
||||
static const StorageDir applicationDocumentsDirectory = StorageDir._(1); |
|
||||
static const StorageDir externalStorageDirectory = StorageDir._(2); |
|
||||
|
|
||||
static const List<StorageDir> values = <StorageDir>[ |
|
||||
temporaryDirectory, |
|
||||
applicationDocumentsDirectory, |
|
||||
externalStorageDirectory, |
|
||||
]; |
|
||||
|
|
||||
@override |
|
||||
String toString() { |
|
||||
return const <int, String>{ |
|
||||
0: 'temporaryDirectory', |
|
||||
1: 'applicationDocumentsDirectory', |
|
||||
2: 'externalStorageDirectory', |
|
||||
}[index]; |
|
||||
} |
|
||||
} |
|
@ -1,81 +0,0 @@ |
|||||
import 'dart:typed_data'; |
|
||||
|
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:video_thumbnail/video_thumbnail.dart'; |
|
||||
|
|
||||
class ThumbnailViewer extends StatelessWidget { |
|
||||
final videoFile; |
|
||||
final videoDuration; |
|
||||
final thumbnailHeight; |
|
||||
final fit; |
|
||||
final int numberOfThumbnails; |
|
||||
final int quality; |
|
||||
|
|
||||
/// For showing the thumbnails generated from the video, |
|
||||
/// like a frame by frame preview |
|
||||
ThumbnailViewer({ |
|
||||
@required this.videoFile, |
|
||||
@required this.videoDuration, |
|
||||
@required this.thumbnailHeight, |
|
||||
@required this.numberOfThumbnails, |
|
||||
@required this.fit, |
|
||||
this.quality = 75, |
|
||||
}) : assert(videoFile != null), |
|
||||
assert(videoDuration != null), |
|
||||
assert(thumbnailHeight != null), |
|
||||
assert(numberOfThumbnails != null), |
|
||||
assert(quality != null); |
|
||||
|
|
||||
Stream<List<Uint8List>> generateThumbnail() async* { |
|
||||
final String _videoPath = videoFile.path; |
|
||||
|
|
||||
double _eachPart = videoDuration / numberOfThumbnails; |
|
||||
|
|
||||
List<Uint8List> _byteList = []; |
|
||||
|
|
||||
for (int i = 1; i <= numberOfThumbnails; i++) { |
|
||||
Uint8List _bytes; |
|
||||
_bytes = await VideoThumbnail.thumbnailData( |
|
||||
video: _videoPath, |
|
||||
imageFormat: ImageFormat.JPEG, |
|
||||
timeMs: (_eachPart * i).toInt(), |
|
||||
quality: quality, |
|
||||
); |
|
||||
|
|
||||
_byteList.add(_bytes); |
|
||||
|
|
||||
yield _byteList; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return StreamBuilder( |
|
||||
stream: generateThumbnail(), |
|
||||
builder: (context, snapshot) { |
|
||||
if (snapshot.hasData) { |
|
||||
List<Uint8List> _imageBytes = snapshot.data; |
|
||||
return ListView.builder( |
|
||||
scrollDirection: Axis.horizontal, |
|
||||
itemCount: snapshot.data.length, |
|
||||
itemBuilder: (context, index) { |
|
||||
return Container( |
|
||||
height: thumbnailHeight, |
|
||||
width: thumbnailHeight, |
|
||||
child: Image( |
|
||||
image: MemoryImage(_imageBytes[index]), |
|
||||
fit: fit, |
|
||||
), |
|
||||
); |
|
||||
}); |
|
||||
} else { |
|
||||
return Container( |
|
||||
color: Colors.grey[900], |
|
||||
height: thumbnailHeight, |
|
||||
width: double.maxFinite, |
|
||||
); |
|
||||
} |
|
||||
}, |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,537 +0,0 @@ |
|||||
import 'dart:io'; |
|
||||
|
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/thumbnail_viewer.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trim_editor_painter.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trimmer.dart'; |
|
||||
import 'package:video_player/video_player.dart'; |
|
||||
|
|
||||
VideoPlayerController videoPlayerController; |
|
||||
|
|
||||
class TrimEditor extends StatefulWidget { |
|
||||
/// For defining the total trimmer area width |
|
||||
final double viewerWidth; |
|
||||
|
|
||||
/// For defining the total trimmer area height |
|
||||
final double viewerHeight; |
|
||||
|
|
||||
/// For defining the image fit type of each thumbnail image. |
|
||||
/// |
|
||||
/// By default it is set to `BoxFit.fitHeight`. |
|
||||
final BoxFit fit; |
|
||||
|
|
||||
/// For defining the maximum length of the output video. |
|
||||
final Duration maxVideoLength; |
|
||||
|
|
||||
/// For specifying a size to the holder at the |
|
||||
/// two ends of the video trimmer area, while it is `idle`. |
|
||||
/// |
|
||||
/// By default it is set to `5.0`. |
|
||||
final double circleSize; |
|
||||
|
|
||||
/// For specifying a size to the holder at |
|
||||
/// the two ends of the video trimmer area, while it is being |
|
||||
/// `dragged`. |
|
||||
/// |
|
||||
/// By default it is set to `8.0`. |
|
||||
final double circleSizeOnDrag; |
|
||||
|
|
||||
/// For specifying a color to the circle. |
|
||||
/// |
|
||||
/// By default it is set to `Colors.white`. |
|
||||
final Color circlePaintColor; |
|
||||
|
|
||||
/// For specifying a color to the border of |
|
||||
/// the trim area. |
|
||||
/// |
|
||||
/// By default it is set to `Colors.white`. |
|
||||
final Color borderPaintColor; |
|
||||
|
|
||||
/// For specifying a color to the video |
|
||||
/// scrubber inside the trim area. |
|
||||
/// |
|
||||
/// By default it is set to `Colors.white`. |
|
||||
final Color scrubberPaintColor; |
|
||||
|
|
||||
/// For specifying the quality of each |
|
||||
/// generated image thumbnail, to be displayed in the trimmer |
|
||||
/// area. |
|
||||
final int thumbnailQuality; |
|
||||
|
|
||||
/// For showing the start and the end point of the |
|
||||
/// video on top of the trimmer area. |
|
||||
/// |
|
||||
/// By default it is set to `true`. |
|
||||
final bool showDuration; |
|
||||
|
|
||||
/// For providing a `TextStyle` to the |
|
||||
/// duration text. |
|
||||
/// |
|
||||
/// By default it is set to `TextStyle(color: Colors.white)` |
|
||||
final TextStyle durationTextStyle; |
|
||||
|
|
||||
/// Callback to the video start position |
|
||||
/// |
|
||||
/// Returns the selected video start position in `milliseconds`. |
|
||||
final Function(double startValue) onChangeStart; |
|
||||
|
|
||||
/// Callback to the video end position. |
|
||||
/// |
|
||||
/// Returns the selected video end position in `milliseconds`. |
|
||||
final Function(double endValue) onChangeEnd; |
|
||||
|
|
||||
/// Callback to the video playback |
|
||||
/// state to know whether it is currently playing or paused. |
|
||||
/// |
|
||||
/// Returns a `boolean` value. If `true`, video is currently |
|
||||
/// playing, otherwise paused. |
|
||||
final Function(bool isPlaying) onChangePlaybackState; |
|
||||
|
|
||||
/// Widget for displaying the video trimmer. |
|
||||
/// |
|
||||
/// This has frame wise preview of the video with a |
|
||||
/// slider for selecting the part of the video to be |
|
||||
/// trimmed. |
|
||||
/// |
|
||||
/// The required parameters are [viewerWidth] & [viewerHeight] |
|
||||
/// |
|
||||
/// * [viewerWidth] to define the total trimmer area width. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [viewerHeight] to define the total trimmer area height. |
|
||||
/// |
|
||||
/// |
|
||||
/// The optional parameters are: |
|
||||
/// |
|
||||
/// * [fit] for specifying the image fit type of each thumbnail image. |
|
||||
/// By default it is set to `BoxFit.fitHeight`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [maxVideoLength] for specifying the maximum length of the |
|
||||
/// output video. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [circleSize] for specifying a size to the holder at the |
|
||||
/// two ends of the video trimmer area, while it is `idle`. |
|
||||
/// By default it is set to `5.0`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [circleSizeOnDrag] for specifying a size to the holder at |
|
||||
/// the two ends of the video trimmer area, while it is being |
|
||||
/// `dragged`. By default it is set to `8.0`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [circlePaintColor] for specifying a color to the circle. |
|
||||
/// By default it is set to `Colors.white`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [borderPaintColor] for specifying a color to the border of |
|
||||
/// the trim area. By default it is set to `Colors.white`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [scrubberPaintColor] for specifying a color to the video |
|
||||
/// scrubber inside the trim area. By default it is set to |
|
||||
/// `Colors.white`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [thumbnailQuality] for specifying the quality of each |
|
||||
/// generated image thumbnail, to be displayed in the trimmer |
|
||||
/// area. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [showDuration] for showing the start and the end point of the |
|
||||
/// video on top of the trimmer area. By default it is set to `true`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [durationTextStyle] is for providing a `TextStyle` to the |
|
||||
/// duration text. By default it is set to |
|
||||
/// `TextStyle(color: Colors.white)` |
|
||||
/// |
|
||||
/// |
|
||||
/// * [onChangeStart] is a callback to the video start position. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [onChangeEnd] is a callback to the video end position. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [onChangePlaybackState] is a callback to the video playback |
|
||||
/// state to know whether it is currently playing or paused. |
|
||||
/// |
|
||||
TrimEditor({ |
|
||||
@required this.viewerWidth, |
|
||||
@required this.viewerHeight, |
|
||||
this.fit = BoxFit.fitHeight, |
|
||||
this.maxVideoLength = const Duration(milliseconds: 0), |
|
||||
this.circleSize = 5.0, |
|
||||
this.circleSizeOnDrag = 8.0, |
|
||||
this.circlePaintColor = Colors.white, |
|
||||
this.borderPaintColor = Colors.white, |
|
||||
this.scrubberPaintColor = Colors.white, |
|
||||
this.thumbnailQuality = 75, |
|
||||
this.showDuration = true, |
|
||||
this.durationTextStyle = const TextStyle( |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
this.onChangeStart, |
|
||||
this.onChangeEnd, |
|
||||
this.onChangePlaybackState, |
|
||||
}) : assert(viewerWidth != null), |
|
||||
assert(viewerHeight != null), |
|
||||
assert(fit != null), |
|
||||
assert(maxVideoLength != null), |
|
||||
assert(circleSize != null), |
|
||||
assert(circleSizeOnDrag != null), |
|
||||
assert(circlePaintColor != null), |
|
||||
assert(borderPaintColor != null), |
|
||||
assert(scrubberPaintColor != null), |
|
||||
assert(thumbnailQuality != null), |
|
||||
assert(showDuration != null), |
|
||||
assert(durationTextStyle != null); |
|
||||
|
|
||||
@override |
|
||||
_TrimEditorState createState() => _TrimEditorState(); |
|
||||
} |
|
||||
|
|
||||
class _TrimEditorState extends State<TrimEditor> with TickerProviderStateMixin { |
|
||||
File _videoFile; |
|
||||
|
|
||||
double _videoStartPos = 0.0; |
|
||||
double _videoEndPos = 0.0; |
|
||||
|
|
||||
bool _canUpdateStart = true; |
|
||||
bool _isLeftDrag = true; |
|
||||
|
|
||||
Offset _startPos = Offset(0, 0); |
|
||||
Offset _endPos = Offset(0, 0); |
|
||||
|
|
||||
double _startFraction = 0.0; |
|
||||
double _endFraction = 1.0; |
|
||||
|
|
||||
int _videoDuration = 0; |
|
||||
int _currentPosition = 0; |
|
||||
|
|
||||
double _thumbnailViewerW = 0.0; |
|
||||
double _thumbnailViewerH = 0.0; |
|
||||
|
|
||||
int _numberOfThumbnails = 0; |
|
||||
|
|
||||
double _circleSize; |
|
||||
|
|
||||
double fraction; |
|
||||
double maxLengthPixels; |
|
||||
|
|
||||
ThumbnailViewer thumbnailWidget; |
|
||||
|
|
||||
Animation<double> _scrubberAnimation; |
|
||||
AnimationController _animationController; |
|
||||
Tween<double> _linearTween; |
|
||||
|
|
||||
Future<void> _initializeVideoController() async { |
|
||||
if (_videoFile != null) { |
|
||||
videoPlayerController.addListener(() { |
|
||||
final bool isPlaying = videoPlayerController.value.isPlaying; |
|
||||
|
|
||||
if (isPlaying) { |
|
||||
widget.onChangePlaybackState(true); |
|
||||
setState(() { |
|
||||
_currentPosition = |
|
||||
videoPlayerController.value.position.inMilliseconds; |
|
||||
|
|
||||
if (_currentPosition > _videoEndPos.toInt()) { |
|
||||
widget.onChangePlaybackState(false); |
|
||||
videoPlayerController.pause(); |
|
||||
_animationController.stop(); |
|
||||
} else { |
|
||||
if (!_animationController.isAnimating) { |
|
||||
widget.onChangePlaybackState(true); |
|
||||
_animationController.forward(); |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
} else { |
|
||||
if (videoPlayerController.value.isInitialized) { |
|
||||
if (_animationController != null) { |
|
||||
if ((_scrubberAnimation.value).toInt() == (_endPos.dx).toInt()) { |
|
||||
_animationController.reset(); |
|
||||
} |
|
||||
_animationController.stop(); |
|
||||
widget.onChangePlaybackState(false); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
videoPlayerController.setVolume(1.0); |
|
||||
_videoDuration = videoPlayerController.value.duration.inMilliseconds; |
|
||||
print(_videoFile.path); |
|
||||
|
|
||||
_videoEndPos = fraction != null |
|
||||
? _videoDuration.toDouble() * fraction |
|
||||
: _videoDuration.toDouble(); |
|
||||
|
|
||||
widget.onChangeEnd(_videoEndPos); |
|
||||
|
|
||||
final ThumbnailViewer _thumbnailWidget = ThumbnailViewer( |
|
||||
videoFile: _videoFile, |
|
||||
videoDuration: _videoDuration, |
|
||||
fit: widget.fit, |
|
||||
thumbnailHeight: _thumbnailViewerH, |
|
||||
numberOfThumbnails: _numberOfThumbnails, |
|
||||
quality: widget.thumbnailQuality, |
|
||||
); |
|
||||
thumbnailWidget = _thumbnailWidget; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void _setVideoStartPosition(DragUpdateDetails details) async { |
|
||||
if (!(_startPos.dx + details.delta.dx < 0) && |
|
||||
!(_startPos.dx + details.delta.dx > _thumbnailViewerW) && |
|
||||
!(_startPos.dx + details.delta.dx > _endPos.dx)) { |
|
||||
if (maxLengthPixels != null) { |
|
||||
if (!(_endPos.dx - _startPos.dx - details.delta.dx > maxLengthPixels)) { |
|
||||
setState(() { |
|
||||
if (!(_startPos.dx + details.delta.dx < 0)) |
|
||||
_startPos += details.delta; |
|
||||
|
|
||||
_startFraction = (_startPos.dx / _thumbnailViewerW); |
|
||||
|
|
||||
_videoStartPos = _videoDuration * _startFraction; |
|
||||
widget.onChangeStart(_videoStartPos); |
|
||||
}); |
|
||||
await videoPlayerController.pause(); |
|
||||
await videoPlayerController |
|
||||
.seekTo(Duration(milliseconds: _videoStartPos.toInt())); |
|
||||
_linearTween.begin = _startPos.dx; |
|
||||
_animationController.duration = |
|
||||
Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()); |
|
||||
_animationController.reset(); |
|
||||
} |
|
||||
} else { |
|
||||
setState(() { |
|
||||
if (!(_startPos.dx + details.delta.dx < 0)) |
|
||||
_startPos += details.delta; |
|
||||
|
|
||||
_startFraction = (_startPos.dx / _thumbnailViewerW); |
|
||||
|
|
||||
_videoStartPos = _videoDuration * _startFraction; |
|
||||
widget.onChangeStart(_videoStartPos); |
|
||||
}); |
|
||||
await videoPlayerController.pause(); |
|
||||
await videoPlayerController |
|
||||
.seekTo(Duration(milliseconds: _videoStartPos.toInt())); |
|
||||
_linearTween.begin = _startPos.dx; |
|
||||
_animationController.duration = |
|
||||
Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()); |
|
||||
_animationController.reset(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void _setVideoEndPosition(DragUpdateDetails details) async { |
|
||||
if (!(_endPos.dx + details.delta.dx > _thumbnailViewerW) && |
|
||||
!(_endPos.dx + details.delta.dx < 0) && |
|
||||
!(_endPos.dx + details.delta.dx < _startPos.dx)) { |
|
||||
if (maxLengthPixels != null) { |
|
||||
if (!(_endPos.dx - _startPos.dx + details.delta.dx > maxLengthPixels)) { |
|
||||
setState(() { |
|
||||
_endPos += details.delta; |
|
||||
_endFraction = _endPos.dx / _thumbnailViewerW; |
|
||||
|
|
||||
_videoEndPos = _videoDuration * _endFraction; |
|
||||
widget.onChangeEnd(_videoEndPos); |
|
||||
}); |
|
||||
await videoPlayerController.pause(); |
|
||||
await videoPlayerController |
|
||||
.seekTo(Duration(milliseconds: _videoEndPos.toInt())); |
|
||||
_linearTween.end = _endPos.dx; |
|
||||
_animationController.duration = |
|
||||
Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()); |
|
||||
_animationController.reset(); |
|
||||
} |
|
||||
} else { |
|
||||
setState(() { |
|
||||
_endPos += details.delta; |
|
||||
_endFraction = _endPos.dx / _thumbnailViewerW; |
|
||||
|
|
||||
_videoEndPos = _videoDuration * _endFraction; |
|
||||
widget.onChangeEnd(_videoEndPos); |
|
||||
}); |
|
||||
await videoPlayerController.pause(); |
|
||||
await videoPlayerController |
|
||||
.seekTo(Duration(milliseconds: _videoEndPos.toInt())); |
|
||||
_linearTween.end = _endPos.dx; |
|
||||
_animationController.duration = |
|
||||
Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()); |
|
||||
_animationController.reset(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
super.initState(); |
|
||||
_circleSize = widget.circleSize; |
|
||||
|
|
||||
_videoFile = Trimmer.currentVideoFile; |
|
||||
_thumbnailViewerH = widget.viewerHeight; |
|
||||
|
|
||||
_numberOfThumbnails = widget.viewerWidth ~/ _thumbnailViewerH; |
|
||||
|
|
||||
_thumbnailViewerW = _numberOfThumbnails * _thumbnailViewerH; |
|
||||
|
|
||||
Duration totalDuration = videoPlayerController.value.duration; |
|
||||
|
|
||||
if (widget.maxVideoLength > Duration(milliseconds: 0) && |
|
||||
widget.maxVideoLength < totalDuration) { |
|
||||
if (widget.maxVideoLength < totalDuration) { |
|
||||
fraction = |
|
||||
widget.maxVideoLength.inMilliseconds / totalDuration.inMilliseconds; |
|
||||
|
|
||||
maxLengthPixels = _thumbnailViewerW * fraction; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
_initializeVideoController(); |
|
||||
_endPos = Offset( |
|
||||
maxLengthPixels != null ? maxLengthPixels : _thumbnailViewerW, |
|
||||
_thumbnailViewerH, |
|
||||
); |
|
||||
|
|
||||
// Defining the tween points |
|
||||
_linearTween = Tween(begin: _startPos.dx, end: _endPos.dx); |
|
||||
|
|
||||
_animationController = AnimationController( |
|
||||
vsync: this, |
|
||||
duration: Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()), |
|
||||
); |
|
||||
|
|
||||
_scrubberAnimation = _linearTween.animate(_animationController) |
|
||||
..addListener(() { |
|
||||
setState(() {}); |
|
||||
}) |
|
||||
..addStatusListener((status) { |
|
||||
if (status == AnimationStatus.completed) { |
|
||||
_animationController.stop(); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() { |
|
||||
videoPlayerController.pause(); |
|
||||
widget.onChangePlaybackState(false); |
|
||||
if (_videoFile != null) { |
|
||||
videoPlayerController.setVolume(0.0); |
|
||||
videoPlayerController.pause(); |
|
||||
videoPlayerController.dispose(); |
|
||||
widget.onChangePlaybackState(false); |
|
||||
} |
|
||||
super.dispose(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return GestureDetector( |
|
||||
onHorizontalDragStart: (DragStartDetails details) { |
|
||||
print("START"); |
|
||||
print(details.localPosition); |
|
||||
print((_startPos.dx - details.localPosition.dx).abs()); |
|
||||
print((_endPos.dx - details.localPosition.dx).abs()); |
|
||||
|
|
||||
if (_endPos.dx >= _startPos.dx) { |
|
||||
if ((_startPos.dx - details.localPosition.dx).abs() > |
|
||||
(_endPos.dx - details.localPosition.dx).abs()) { |
|
||||
setState(() { |
|
||||
_canUpdateStart = false; |
|
||||
}); |
|
||||
} else { |
|
||||
setState(() { |
|
||||
_canUpdateStart = true; |
|
||||
}); |
|
||||
} |
|
||||
} else { |
|
||||
if (_startPos.dx > details.localPosition.dx) { |
|
||||
_isLeftDrag = true; |
|
||||
} else { |
|
||||
_isLeftDrag = false; |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
onHorizontalDragEnd: (DragEndDetails details) { |
|
||||
setState(() { |
|
||||
_circleSize = widget.circleSize; |
|
||||
}); |
|
||||
}, |
|
||||
onHorizontalDragUpdate: (DragUpdateDetails details) { |
|
||||
_circleSize = widget.circleSizeOnDrag; |
|
||||
|
|
||||
if (_endPos.dx >= _startPos.dx) { |
|
||||
_isLeftDrag = false; |
|
||||
if (_canUpdateStart && _startPos.dx + details.delta.dx > 0) { |
|
||||
_isLeftDrag = false; // To prevent from scrolling over |
|
||||
_setVideoStartPosition(details); |
|
||||
} else if (!_canUpdateStart && |
|
||||
_endPos.dx + details.delta.dx < _thumbnailViewerW) { |
|
||||
_isLeftDrag = true; // To prevent from scrolling over |
|
||||
_setVideoEndPosition(details); |
|
||||
} |
|
||||
} else { |
|
||||
if (_isLeftDrag && _startPos.dx + details.delta.dx > 0) { |
|
||||
_setVideoStartPosition(details); |
|
||||
} else if (!_isLeftDrag && |
|
||||
_endPos.dx + details.delta.dx < _thumbnailViewerW) { |
|
||||
_setVideoEndPosition(details); |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
child: Column( |
|
||||
mainAxisSize: MainAxisSize.min, |
|
||||
children: <Widget>[ |
|
||||
widget.showDuration |
|
||||
? Container( |
|
||||
width: _thumbnailViewerW, |
|
||||
child: Padding( |
|
||||
padding: const EdgeInsets.only(bottom: 8.0), |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
||||
mainAxisSize: MainAxisSize.max, |
|
||||
children: <Widget>[ |
|
||||
Text( |
|
||||
Duration(milliseconds: _videoStartPos.toInt()) |
|
||||
.toString() |
|
||||
.split('.')[0], |
|
||||
style: widget.durationTextStyle, |
|
||||
), |
|
||||
Text( |
|
||||
Duration(milliseconds: _videoEndPos.toInt()) |
|
||||
.toString() |
|
||||
.split('.')[0], |
|
||||
style: widget.durationTextStyle, |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
) |
|
||||
: Container(), |
|
||||
CustomPaint( |
|
||||
foregroundPainter: TrimEditorPainter( |
|
||||
startPos: _startPos, |
|
||||
endPos: _endPos, |
|
||||
scrubberAnimationDx: _scrubberAnimation.value, |
|
||||
circleSize: _circleSize, |
|
||||
circlePaintColor: widget.circlePaintColor, |
|
||||
borderPaintColor: widget.borderPaintColor, |
|
||||
scrubberPaintColor: widget.scrubberPaintColor, |
|
||||
), |
|
||||
child: Container( |
|
||||
color: Colors.grey[900], |
|
||||
height: _thumbnailViewerH, |
|
||||
width: _thumbnailViewerW, |
|
||||
child: thumbnailWidget == null ? Column() : thumbnailWidget, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,150 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
class TrimEditorPainter extends CustomPainter { |
|
||||
/// To define the start offset |
|
||||
final Offset startPos; |
|
||||
|
|
||||
/// To define the end offset |
|
||||
final Offset endPos; |
|
||||
|
|
||||
/// To define the horizontal length of the selected video area |
|
||||
final double scrubberAnimationDx; |
|
||||
|
|
||||
/// For specifying a size to the holder at the |
|
||||
/// two ends of the video trimmer area, while it is `idle`. |
|
||||
/// By default it is set to `0.5`. |
|
||||
final double circleSize; |
|
||||
|
|
||||
/// For specifying the width of the border around |
|
||||
/// the trim area. By default it is set to `3`. |
|
||||
final double borderWidth; |
|
||||
|
|
||||
/// For specifying the width of the video scrubber |
|
||||
final double scrubberWidth; |
|
||||
|
|
||||
/// For specifying whether to show the scrubber |
|
||||
final bool showScrubber; |
|
||||
|
|
||||
/// For specifying a color to the border of |
|
||||
/// the trim area. By default it is set to `Colors.white`. |
|
||||
final Color borderPaintColor; |
|
||||
|
|
||||
/// For specifying a color to the circle. |
|
||||
/// By default it is set to `Colors.white` |
|
||||
final Color circlePaintColor; |
|
||||
|
|
||||
/// For specifying a color to the video |
|
||||
/// scrubber inside the trim area. By default it is set to |
|
||||
/// `Colors.white`. |
|
||||
final Color scrubberPaintColor; |
|
||||
|
|
||||
/// For drawing the trim editor slider |
|
||||
/// |
|
||||
/// The required parameters are [startPos], [endPos] |
|
||||
/// & [scrubberAnimationDx] |
|
||||
/// |
|
||||
/// * [startPos] to define the start offset |
|
||||
/// |
|
||||
/// |
|
||||
/// * [endPos] to define the end offset |
|
||||
/// |
|
||||
/// |
|
||||
/// * [scrubberAnimationDx] to define the horizontal length of the |
|
||||
/// selected video area |
|
||||
/// |
|
||||
/// |
|
||||
/// The optional parameters are: |
|
||||
/// |
|
||||
/// * [circleSize] for specifying a size to the holder at the |
|
||||
/// two ends of the video trimmer area, while it is `idle`. |
|
||||
/// By default it is set to `0.5`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [borderWidth] for specifying the width of the border around |
|
||||
/// the trim area. By default it is set to `3`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [scrubberWidth] for specifying the width of the video scrubber |
|
||||
/// |
|
||||
/// |
|
||||
/// * [showScrubber] for specifying whether to show the scrubber |
|
||||
/// |
|
||||
/// |
|
||||
/// * [borderPaintColor] for specifying a color to the border of |
|
||||
/// the trim area. By default it is set to `Colors.white`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [circlePaintColor] for specifying a color to the circle. |
|
||||
/// By default it is set to `Colors.white`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [scrubberPaintColor] for specifying a color to the video |
|
||||
/// scrubber inside the trim area. By default it is set to |
|
||||
/// `Colors.white`. |
|
||||
/// |
|
||||
TrimEditorPainter({ |
|
||||
@required this.startPos, |
|
||||
@required this.endPos, |
|
||||
@required this.scrubberAnimationDx, |
|
||||
this.circleSize = 0.5, |
|
||||
this.borderWidth = 3, |
|
||||
this.scrubberWidth = 1, |
|
||||
this.showScrubber = true, |
|
||||
this.borderPaintColor = Colors.white, |
|
||||
this.circlePaintColor = Colors.white, |
|
||||
this.scrubberPaintColor = Colors.white, |
|
||||
}) : assert(startPos != null), |
|
||||
assert(endPos != null), |
|
||||
assert(scrubberAnimationDx != null), |
|
||||
assert(circleSize != null), |
|
||||
assert(borderWidth != null), |
|
||||
assert(scrubberWidth != null), |
|
||||
assert(showScrubber != null), |
|
||||
assert(borderPaintColor != null), |
|
||||
assert(circlePaintColor != null), |
|
||||
assert(scrubberPaintColor != null); |
|
||||
|
|
||||
@override |
|
||||
void paint(Canvas canvas, Size size) { |
|
||||
var borderPaint = Paint() |
|
||||
..color = borderPaintColor |
|
||||
..strokeWidth = borderWidth |
|
||||
..style = PaintingStyle.stroke |
|
||||
..strokeCap = StrokeCap.round; |
|
||||
|
|
||||
var circlePaint = Paint() |
|
||||
..color = circlePaintColor |
|
||||
..strokeWidth = 1 |
|
||||
..style = PaintingStyle.fill |
|
||||
..strokeCap = StrokeCap.round; |
|
||||
|
|
||||
var scrubberPaint = Paint() |
|
||||
..color = scrubberPaintColor |
|
||||
..strokeWidth = scrubberWidth |
|
||||
..style = PaintingStyle.stroke |
|
||||
..strokeCap = StrokeCap.round; |
|
||||
|
|
||||
final rect = Rect.fromPoints(startPos, endPos); |
|
||||
|
|
||||
if (showScrubber) { |
|
||||
if (scrubberAnimationDx.toInt() > startPos.dx.toInt()) { |
|
||||
canvas.drawLine( |
|
||||
Offset(scrubberAnimationDx, 0), |
|
||||
Offset(scrubberAnimationDx, 0) + Offset(0, endPos.dy), |
|
||||
scrubberPaint, |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
canvas.drawRect(rect, borderPaint); |
|
||||
canvas.drawCircle( |
|
||||
startPos + Offset(0, endPos.dy / 2), circleSize, circlePaint); |
|
||||
canvas.drawCircle( |
|
||||
endPos + Offset(0, -endPos.dy / 2), circleSize, circlePaint); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
bool shouldRepaint(CustomPainter oldDelegate) { |
|
||||
return true; |
|
||||
} |
|
||||
} |
|
@ -1,300 +0,0 @@ |
|||||
import 'dart:io'; |
|
||||
import 'package:path/path.dart'; |
|
||||
|
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter_ffmpeg/flutter_ffmpeg.dart'; |
|
||||
import 'package:intl/intl.dart'; |
|
||||
import 'package:path_provider/path_provider.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/file_formats.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/storage_dir.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trim_editor.dart'; |
|
||||
import 'package:video_player/video_player.dart'; |
|
||||
|
|
||||
/// Helps in loading video from file, saving trimmed video to a file |
|
||||
/// and gives video playback controls. Some of the helpful methods |
|
||||
/// are: |
|
||||
/// * [loadVideo()] |
|
||||
/// * [saveTrimmedVideo()] |
|
||||
/// * [videPlaybackControl()] |
|
||||
class Trimmer { |
|
||||
static File currentVideoFile; |
|
||||
|
|
||||
final FlutterFFmpeg _flutterFFmpeg = new FlutterFFmpeg(); |
|
||||
|
|
||||
/// Loads a video using the path provided. |
|
||||
/// |
|
||||
/// Returns the loaded video file. |
|
||||
Future<void> loadVideo({@required File videoFile}) async { |
|
||||
currentVideoFile = videoFile; |
|
||||
if (currentVideoFile != null) { |
|
||||
videoPlayerController = VideoPlayerController.file(currentVideoFile); |
|
||||
await videoPlayerController.initialize().then((_) { |
|
||||
TrimEditor( |
|
||||
viewerHeight: 50, |
|
||||
viewerWidth: 50.0 * 8, |
|
||||
// currentVideoFile: currentVideoFile, |
|
||||
); |
|
||||
}); |
|
||||
// TrimEditor( |
|
||||
// viewerHeight: 50, |
|
||||
// viewerWidth: 50.0 * 8, |
|
||||
// // currentVideoFile: currentVideoFile, |
|
||||
// ); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Future<String> _createFolderInAppDocDir( |
|
||||
String folderName, |
|
||||
StorageDir storageDir, |
|
||||
) async { |
|
||||
Directory _directory; |
|
||||
|
|
||||
if (storageDir == null) { |
|
||||
_directory = await getApplicationDocumentsDirectory(); |
|
||||
} else { |
|
||||
switch (storageDir.toString()) { |
|
||||
case 'temporaryDirectory': |
|
||||
_directory = await getTemporaryDirectory(); |
|
||||
break; |
|
||||
|
|
||||
case 'applicationDocumentsDirectory': |
|
||||
_directory = await getApplicationDocumentsDirectory(); |
|
||||
break; |
|
||||
|
|
||||
case 'externalStorageDirectory': |
|
||||
_directory = await getExternalStorageDirectory(); |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Directory + folder name |
|
||||
final Directory _directoryFolder = |
|
||||
Directory('${_directory.path}/$folderName/'); |
|
||||
|
|
||||
if (await _directoryFolder.exists()) { |
|
||||
// If folder already exists return path |
|
||||
print('Exists'); |
|
||||
return _directoryFolder.path; |
|
||||
} else { |
|
||||
print('Creating'); |
|
||||
// If folder does not exists create folder and then return its path |
|
||||
final Directory _directoryNewFolder = |
|
||||
await _directoryFolder.create(recursive: true); |
|
||||
return _directoryNewFolder.path; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// Saves the trimmed video to file system. |
|
||||
/// |
|
||||
/// Returns the output video path |
|
||||
/// |
|
||||
/// The required parameters are [startValue] & [endValue]. |
|
||||
/// |
|
||||
/// The optional parameters are [videoFolderName], [videoFileName], |
|
||||
/// [outputFormat], [fpsGIF], [scaleGIF], [applyVideoEncoding]. |
|
||||
/// |
|
||||
/// The `@required` parameter [startValue] is for providing a starting point |
|
||||
/// to the trimmed video. To be specified in `milliseconds`. |
|
||||
/// |
|
||||
/// The `@required` parameter [endValue] is for providing an ending point |
|
||||
/// to the trimmed video. To be specified in `milliseconds`. |
|
||||
/// |
|
||||
/// The parameter [videoFolderName] is used to |
|
||||
/// pass a folder name which will be used for creating a new |
|
||||
/// folder in the selected directory. The default value for |
|
||||
/// it is `Trimmer`. |
|
||||
/// |
|
||||
/// The parameter [videoFileName] is used for giving |
|
||||
/// a new name to the trimmed video file. By default the |
|
||||
/// trimmed video is named as `<original_file_name>_trimmed.mp4`. |
|
||||
/// |
|
||||
/// The parameter [outputFormat] is used for providing a |
|
||||
/// file format to the trimmed video. This only accepts value |
|
||||
/// of [FileFormat] type. By default it is set to `FileFormat.mp4`, |
|
||||
/// which is for `mp4` files. |
|
||||
/// |
|
||||
/// The parameter [storageDir] can be used for providing a storage |
|
||||
/// location option. It accepts only [StorageDir] values. By default |
|
||||
/// it is set to [applicationDocumentsDirectory]. Some of the |
|
||||
/// storage types are: |
|
||||
/// |
|
||||
/// * [temporaryDirectory] (Only accessible from inside the app, can be |
|
||||
/// cleared at anytime) |
|
||||
/// |
|
||||
/// * [applicationDocumentsDirectory] (Only accessible from inside the app) |
|
||||
/// |
|
||||
/// * [externalStorageDirectory] (Supports only `Android`, accessible externally) |
|
||||
/// |
|
||||
/// The parameters [fpsGIF] & [scaleGIF] are used only if the |
|
||||
/// selected output format is `FileFormat.gif`. |
|
||||
/// |
|
||||
/// * [fpsGIF] for providing a FPS value (by default it is set |
|
||||
/// to `10`) |
|
||||
/// |
|
||||
/// |
|
||||
/// * [scaleGIF] for proving a width to output GIF, the height |
|
||||
/// is selected by maintaining the aspect ratio automatically (by |
|
||||
/// default it is set to `480`) |
|
||||
/// |
|
||||
/// |
|
||||
/// * [applyVideoEncoding] for specifying whether to apply video |
|
||||
/// encoding (by default it is set to `false`). |
|
||||
/// |
|
||||
/// |
|
||||
/// ADVANCED OPTION: |
|
||||
/// |
|
||||
/// If you want to give custom `FFmpeg` command, then define |
|
||||
/// [ffmpegCommand] & [customVideoFormat] strings. The `input path`, |
|
||||
/// `output path`, `start` and `end` position is already define. |
|
||||
/// |
|
||||
/// NOTE: The advanced option does not provide any safety check, so if wrong |
|
||||
/// video format is passed in [customVideoFormat], then the app may |
|
||||
/// crash. |
|
||||
/// |
|
||||
Future<String> saveTrimmedVideo({ |
|
||||
@required double startValue, |
|
||||
@required double endValue, |
|
||||
bool applyVideoEncoding = false, |
|
||||
FileFormat outputFormat, |
|
||||
String ffmpegCommand, |
|
||||
String customVideoFormat, |
|
||||
int fpsGIF, |
|
||||
int scaleGIF, |
|
||||
String videoFolderName, |
|
||||
String videoFileName, |
|
||||
StorageDir storageDir, |
|
||||
}) async { |
|
||||
final String _videoPath = currentVideoFile.path; |
|
||||
final String _videoName = basename(_videoPath).split('.')[0]; |
|
||||
|
|
||||
String _command; |
|
||||
|
|
||||
// Formatting Date and Time |
|
||||
String dateTime = DateFormat.yMMMd() |
|
||||
.addPattern('-') |
|
||||
.add_Hms() |
|
||||
.format(DateTime.now()) |
|
||||
.toString(); |
|
||||
|
|
||||
// String _resultString; |
|
||||
String _outputPath; |
|
||||
String _outputFormatString; |
|
||||
String formattedDateTime = dateTime.replaceAll(' ', ''); |
|
||||
|
|
||||
print("DateTime: $dateTime"); |
|
||||
print("Formatted: $formattedDateTime"); |
|
||||
|
|
||||
if (videoFolderName == null) { |
|
||||
videoFolderName = "Trimmer"; |
|
||||
} |
|
||||
|
|
||||
if (videoFileName == null) { |
|
||||
videoFileName = "${_videoName}_trimmed:$formattedDateTime"; |
|
||||
} |
|
||||
|
|
||||
videoFileName = videoFileName.replaceAll(' ', '_'); |
|
||||
|
|
||||
String path = await _createFolderInAppDocDir( |
|
||||
videoFolderName, |
|
||||
storageDir, |
|
||||
).whenComplete( |
|
||||
() => print("Retrieved Trimmer folder"), |
|
||||
); |
|
||||
|
|
||||
Duration startPoint = Duration(milliseconds: startValue.toInt()); |
|
||||
Duration endPoint = Duration(milliseconds: endValue.toInt()); |
|
||||
|
|
||||
// Checking the start and end point strings |
|
||||
print("Start: ${startPoint.toString()} & End: ${endPoint.toString()}"); |
|
||||
|
|
||||
print(path); |
|
||||
|
|
||||
if (outputFormat == null) { |
|
||||
if (Platform.isIOS) { |
|
||||
outputFormat = FileFormat.mp4; |
|
||||
} else { |
|
||||
outputFormat = FileFormat.mkv; |
|
||||
} |
|
||||
_outputFormatString = outputFormat.toString(); |
|
||||
print('OUTPUT: $_outputFormatString'); |
|
||||
} else { |
|
||||
_outputFormatString = outputFormat.toString(); |
|
||||
} |
|
||||
|
|
||||
String _trimLengthCommand = |
|
||||
' -ss $startPoint -i "$_videoPath" -t ${endPoint - startPoint} -avoid_negative_ts make_zero '; |
|
||||
|
|
||||
if (ffmpegCommand == null) { |
|
||||
_command = '$_trimLengthCommand -c:a copy '; |
|
||||
|
|
||||
if (!applyVideoEncoding) { |
|
||||
_command += '-c:v copy '; |
|
||||
} |
|
||||
|
|
||||
if (outputFormat == FileFormat.gif) { |
|
||||
if (fpsGIF == null) { |
|
||||
fpsGIF = 10; |
|
||||
} |
|
||||
if (scaleGIF == null) { |
|
||||
scaleGIF = 480; |
|
||||
} |
|
||||
_command = |
|
||||
'$_trimLengthCommand -vf "fps=$fpsGIF,scale=$scaleGIF:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 '; |
|
||||
} |
|
||||
} else { |
|
||||
_command = '$_trimLengthCommand $ffmpegCommand '; |
|
||||
_outputFormatString = customVideoFormat; |
|
||||
} |
|
||||
|
|
||||
_outputPath = '$path$videoFileName$_outputFormatString'; |
|
||||
|
|
||||
_command += '"$_outputPath"'; |
|
||||
print(_command); |
|
||||
await _flutterFFmpeg.execute(_command).whenComplete(() { |
|
||||
print('Got value'); |
|
||||
debugPrint('Video successfuly saved'); |
|
||||
// _resultString = 'Video successfuly saved'; |
|
||||
}).catchError((error) { |
|
||||
print('Error'); |
|
||||
// _resultString = 'Couldn\'t save the video'; |
|
||||
debugPrint('Couldn\'t save the video'); |
|
||||
}); |
|
||||
|
|
||||
return _outputPath; |
|
||||
} |
|
||||
|
|
||||
/// For getting the video controller state, to know whether the |
|
||||
/// video is playing or paused currently. |
|
||||
/// |
|
||||
/// The two required parameters are [startValue] & [endValue] |
|
||||
/// |
|
||||
/// * [startValue] is the current starting point of the video. |
|
||||
/// * [endValue] is the current ending point of the video. |
|
||||
/// |
|
||||
/// Returns a `Future<bool>`, if `true` then video is playing |
|
||||
/// otherwise paused. |
|
||||
Future<bool> videPlaybackControl({ |
|
||||
@required double startValue, |
|
||||
@required double endValue, |
|
||||
}) async { |
|
||||
if (videoPlayerController.value.isPlaying) { |
|
||||
await videoPlayerController.pause(); |
|
||||
return false; |
|
||||
} else { |
|
||||
if (videoPlayerController.value.position.inMilliseconds >= |
|
||||
endValue.toInt()) { |
|
||||
await videoPlayerController |
|
||||
.seekTo(Duration(milliseconds: startValue.toInt())); |
|
||||
await videoPlayerController.play(); |
|
||||
return true; |
|
||||
} else { |
|
||||
await videoPlayerController.play(); |
|
||||
return true; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
File getVideoFile() { |
|
||||
return currentVideoFile; |
|
||||
} |
|
||||
} |
|
@ -1,153 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/Campaign.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/Campaign/Video/RecordVideo.dart'; |
|
||||
import 'package:teso/providers/pageAnimations.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
|
|
||||
class Audition extends StatefulWidget { |
|
||||
final Campaign campaign; |
|
||||
|
|
||||
const Audition({Key key, this.campaign}) : super(key: key); |
|
||||
@override |
|
||||
_AuditionState createState() => _AuditionState(); |
|
||||
} |
|
||||
|
|
||||
class _AuditionState extends State<Audition> { |
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Scaffold( |
|
||||
appBar: AppBar( |
|
||||
title: Text( |
|
||||
widget.campaign.title, |
|
||||
), |
|
||||
centerTitle: true, |
|
||||
), |
|
||||
body: Padding( |
|
||||
padding: const EdgeInsets.all(8.0), |
|
||||
child: ListView( |
|
||||
scrollDirection: Axis.vertical, |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: 150, |
|
||||
child: Center( |
|
||||
child: Container( |
|
||||
width: 150, |
|
||||
height: 150, |
|
||||
decoration: BoxDecoration( |
|
||||
border: Border.all( |
|
||||
color: Colors.grey, |
|
||||
width: 0.5, |
|
||||
), |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topRight: Radius.circular(30), |
|
||||
topLeft: Radius.circular(30), |
|
||||
bottomLeft: Radius.circular(30), |
|
||||
bottomRight: Radius.circular(30), |
|
||||
), |
|
||||
), |
|
||||
child: ClipRRect( |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topLeft: Radius.circular(30.0), |
|
||||
topRight: Radius.circular(30.0), |
|
||||
bottomLeft: Radius.circular(30), |
|
||||
bottomRight: Radius.circular(30), |
|
||||
), |
|
||||
child: Image( |
|
||||
width: MediaQuery.of(context).size.width * 0.28, |
|
||||
height: 110, |
|
||||
fit: BoxFit.fill, |
|
||||
image: NetworkImage(widget.campaign.targetProduct != null |
|
||||
? productURL + widget.campaign.targetProduct |
|
||||
: ""), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
SizedBox( |
|
||||
height: 20, |
|
||||
), |
|
||||
Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: Center( |
|
||||
child: new RichText( |
|
||||
text: new TextSpan( |
|
||||
style: new TextStyle( |
|
||||
fontSize: 14.0, |
|
||||
color: Theme.of(context).primaryColorLight, |
|
||||
), |
|
||||
children: <TextSpan>[ |
|
||||
new TextSpan( |
|
||||
text: "Publisher : ", |
|
||||
style: new TextStyle( |
|
||||
fontWeight: FontWeight.bold, |
|
||||
), |
|
||||
), |
|
||||
new TextSpan( |
|
||||
text: widget.campaign.businessID, |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
SizedBox( |
|
||||
height: 10, |
|
||||
), |
|
||||
Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: Center( |
|
||||
child: Text( |
|
||||
"Description", |
|
||||
style: TextStyle( |
|
||||
fontWeight: FontWeight.bold, |
|
||||
fontSize: 16.5, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: Center( |
|
||||
child: Text(widget.campaign.description), |
|
||||
), |
|
||||
), |
|
||||
SizedBox( |
|
||||
height: 30, |
|
||||
), |
|
||||
Container( |
|
||||
width: double.infinity, |
|
||||
child: Align( |
|
||||
alignment: Alignment.center, |
|
||||
child: Container( |
|
||||
width: 100, |
|
||||
child: ElevatedButton( |
|
||||
style: ElevatedButton.styleFrom( |
|
||||
shape: RoundedRectangleBorder( |
|
||||
borderRadius: BorderRadius.all( |
|
||||
Radius.circular(20.0), |
|
||||
), |
|
||||
), |
|
||||
primary: accentMain, |
|
||||
), |
|
||||
onPressed: () => Navigator.pushReplacement( |
|
||||
context, |
|
||||
PageTransition( |
|
||||
child: RecordVideo( |
|
||||
campaignID: widget.campaign.campaignID, |
|
||||
), |
|
||||
type: PageTransitionType.fade, |
|
||||
), |
|
||||
), |
|
||||
child: Text("Submit Ad"), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,200 +0,0 @@ |
|||||
import 'dart:convert'; |
|
||||
import 'dart:typed_data'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter/cupertino.dart'; |
|
||||
import 'package:provider/provider.dart'; |
|
||||
import 'package:shared_preferences/shared_preferences.dart'; |
|
||||
import 'package:teso/Classes/Uploading.dart'; |
|
||||
import 'package:teso/providers/user_provider.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
import 'package:http/http.dart' as http; |
|
||||
|
|
||||
class CreateCampaignPost extends StatefulWidget { |
|
||||
final String video; |
|
||||
final Uint8List thumbnail; |
|
||||
final String campaignID; |
|
||||
final String aspectRatio; |
|
||||
|
|
||||
const CreateCampaignPost({ |
|
||||
Key key, |
|
||||
this.video, |
|
||||
this.thumbnail, |
|
||||
this.campaignID, |
|
||||
this.aspectRatio, |
|
||||
}) : super(key: key); |
|
||||
@override |
|
||||
_CreateCampaignPostState createState() => _CreateCampaignPostState(); |
|
||||
} |
|
||||
|
|
||||
class _CreateCampaignPostState extends State<CreateCampaignPost> { |
|
||||
String aspectRatio; |
|
||||
TextEditingController controller; |
|
||||
SharedPreferences prefs; |
|
||||
bool sending = false; |
|
||||
|
|
||||
void postVideo(context) async { |
|
||||
setState(() { |
|
||||
sending = true; |
|
||||
}); |
|
||||
try { |
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance(); |
|
||||
String token = prefs.getString("tokensTeso"); |
|
||||
Map<String, String> requestHeaders = {'Authorization': token}; |
|
||||
String urlLocation = tesoStreaming + "api/mobile/upload/authurl"; |
|
||||
var client = |
|
||||
await http.get(Uri.parse(urlLocation), headers: requestHeaders); |
|
||||
if (client.statusCode == 200) { |
|
||||
var details = jsonDecode(client.body); |
|
||||
String muxuploadsID = details["data"]["id"]; |
|
||||
String muxuploadsURL = details["data"]["url"]; |
|
||||
|
|
||||
Provider.of<UserProvider>(context, listen: false).uploadPost(Uploading( |
|
||||
id: DateTime.now().toString() + |
|
||||
widget.video.replaceAll("file://", ""), |
|
||||
aspect: widget.aspectRatio, |
|
||||
path: widget.video.replaceAll("file://", ""), |
|
||||
thumbnail: |
|
||||
widget.thumbnail != null ? base64Encode(widget.thumbnail) : null, |
|
||||
title: controller.text, |
|
||||
pending: 0, |
|
||||
campaignID: widget.campaignID, |
|
||||
muxuploadID: muxuploadsID, |
|
||||
muxuploadURL: muxuploadsURL, |
|
||||
)); |
|
||||
Navigator.pop(context); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
print(e); |
|
||||
} |
|
||||
setState(() { |
|
||||
sending = false; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
super.initState(); |
|
||||
controller = new TextEditingController(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() { |
|
||||
super.dispose(); |
|
||||
controller.dispose(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Scaffold( |
|
||||
appBar: PreferredSize( |
|
||||
child: AppBar( |
|
||||
automaticallyImplyLeading: true, |
|
||||
title: Text("Post"), |
|
||||
centerTitle: true, |
|
||||
), |
|
||||
preferredSize: Size.fromHeight(70.0), |
|
||||
), |
|
||||
body: SingleChildScrollView( |
|
||||
child: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
padding: EdgeInsets.all(MediaQuery.of(context).size.width * 0.025), |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: MediaQuery.of(context).size.width * 0.25, |
|
||||
height: MediaQuery.of(context).size.width * 0.35, |
|
||||
color: Colors.black, |
|
||||
child: widget.thumbnail != null |
|
||||
? Image.memory(widget.thumbnail) |
|
||||
: CupertinoActivityIndicator( |
|
||||
animating: true, |
|
||||
radius: 15, |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
width: (MediaQuery.of(context).size.width) - |
|
||||
(MediaQuery.of(context).size.width * 0.35), |
|
||||
height: MediaQuery.of(context).size.width * 0.35, |
|
||||
child: TextField( |
|
||||
decoration: InputDecoration( |
|
||||
border: OutlineInputBorder( |
|
||||
borderSide: BorderSide.none, |
|
||||
), |
|
||||
filled: true, |
|
||||
isDense: true, |
|
||||
labelText: "Say Something..", |
|
||||
labelStyle: TextStyle( |
|
||||
color: Colors.black54, |
|
||||
), |
|
||||
fillColor: Colors.white70, |
|
||||
), |
|
||||
controller: controller, |
|
||||
maxLines: null, |
|
||||
keyboardType: TextInputType.text, |
|
||||
textInputAction: TextInputAction.done, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
SizedBox( |
|
||||
height: 20, |
|
||||
), |
|
||||
Divider(), |
|
||||
Container( |
|
||||
margin: EdgeInsets.only( |
|
||||
top: 10, |
|
||||
), |
|
||||
child: Text( |
|
||||
"Teso businesses and other Teso users can see your post in their feeds and on your profile.", |
|
||||
textAlign: TextAlign.center, |
|
||||
style: TextStyle( |
|
||||
color: Colors.grey, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
floatingActionButton: !sending |
|
||||
? Container( |
|
||||
margin: EdgeInsets.all(20), |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
decoration: BoxDecoration( |
|
||||
borderRadius: BorderRadius.circular(15.0), |
|
||||
color: tesoBlue, |
|
||||
), |
|
||||
child: InkWell( |
|
||||
onTap: () => postVideo(context), |
|
||||
child: Center( |
|
||||
child: Text( |
|
||||
"NEXT", |
|
||||
style: TextStyle( |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
height: 50, |
|
||||
) |
|
||||
: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: 50, |
|
||||
child: Center( |
|
||||
child: CupertinoActivityIndicator( |
|
||||
animating: true, |
|
||||
radius: 15, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,195 +0,0 @@ |
|||||
import 'dart:convert'; |
|
||||
import 'dart:typed_data'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter/cupertino.dart'; |
|
||||
import 'package:provider/provider.dart'; |
|
||||
import 'package:shared_preferences/shared_preferences.dart'; |
|
||||
import 'package:teso/Classes/Uploading.dart'; |
|
||||
import 'package:teso/providers/user_provider.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
import 'package:http/http.dart' as http; |
|
||||
|
|
||||
class SubmitAdvert extends StatefulWidget { |
|
||||
final String video; |
|
||||
final String aspectRatio; |
|
||||
final Uint8List thumbnail; |
|
||||
final String campaignID; |
|
||||
|
|
||||
const SubmitAdvert( |
|
||||
{Key key, this.video, this.aspectRatio, this.thumbnail, this.campaignID}) |
|
||||
: super(key: key); |
|
||||
@override |
|
||||
_SubmitAdvertState createState() => _SubmitAdvertState(); |
|
||||
} |
|
||||
|
|
||||
class _SubmitAdvertState extends State<SubmitAdvert> { |
|
||||
TextEditingController controller; |
|
||||
SharedPreferences prefs; |
|
||||
bool sending = false; |
|
||||
|
|
||||
void postVideo(context) async { |
|
||||
setState(() { |
|
||||
sending = true; |
|
||||
}); |
|
||||
try { |
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance(); |
|
||||
String token = prefs.getString("tokensTeso"); |
|
||||
Map<String, String> requestHeaders = {'Authorization': token}; |
|
||||
String urlLocation = tesoStreaming + "api/mobile/upload/authurl"; |
|
||||
var client = |
|
||||
await http.get(Uri.parse(urlLocation), headers: requestHeaders); |
|
||||
if (client.statusCode == 200) { |
|
||||
var details = jsonDecode(client.body); |
|
||||
String muxuploadsID = details["data"]["id"]; |
|
||||
String muxuploadsURL = details["data"]["url"]; |
|
||||
|
|
||||
Provider.of<UserProvider>(context, listen: false).uploadPost(Uploading( |
|
||||
id: DateTime.now().toString() + |
|
||||
widget.video.replaceAll("file://", ""), |
|
||||
aspect: widget.aspectRatio, |
|
||||
path: widget.video.replaceAll("file://", ""), |
|
||||
thumbnail: |
|
||||
widget.thumbnail != null ? base64Encode(widget.thumbnail) : null, |
|
||||
title: controller.text, |
|
||||
pending: 0, |
|
||||
campaignID: widget.campaignID, |
|
||||
muxuploadID: muxuploadsID, |
|
||||
muxuploadURL: muxuploadsURL, |
|
||||
)); |
|
||||
Navigator.pop(context); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
print("Something is " + e.toString()); |
|
||||
} |
|
||||
setState(() { |
|
||||
sending = false; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
super.initState(); |
|
||||
controller = new TextEditingController(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() { |
|
||||
super.dispose(); |
|
||||
controller.dispose(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Scaffold( |
|
||||
appBar: PreferredSize( |
|
||||
child: AppBar( |
|
||||
automaticallyImplyLeading: true, |
|
||||
title: Text("Post"), |
|
||||
centerTitle: true, |
|
||||
), |
|
||||
preferredSize: Size.fromHeight(70.0), |
|
||||
), |
|
||||
body: SingleChildScrollView( |
|
||||
child: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
padding: EdgeInsets.all(MediaQuery.of(context).size.width * 0.025), |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: MediaQuery.of(context).size.width * 0.25, |
|
||||
height: MediaQuery.of(context).size.width * 0.35, |
|
||||
color: Colors.black, |
|
||||
child: widget.thumbnail != null |
|
||||
? Image.memory(widget.thumbnail) |
|
||||
: CupertinoActivityIndicator( |
|
||||
animating: true, |
|
||||
radius: 15, |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
width: (MediaQuery.of(context).size.width) - |
|
||||
(MediaQuery.of(context).size.width * 0.35), |
|
||||
height: MediaQuery.of(context).size.width * 0.35, |
|
||||
child: TextField( |
|
||||
decoration: InputDecoration( |
|
||||
border: OutlineInputBorder( |
|
||||
borderSide: BorderSide.none, |
|
||||
), |
|
||||
filled: true, |
|
||||
isDense: true, |
|
||||
labelText: "Say Something..", |
|
||||
labelStyle: TextStyle( |
|
||||
color: Colors.black54, |
|
||||
), |
|
||||
fillColor: Colors.white70, |
|
||||
), |
|
||||
controller: controller, |
|
||||
maxLines: null, |
|
||||
keyboardType: TextInputType.url, |
|
||||
textInputAction: TextInputAction.done, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
SizedBox( |
|
||||
height: 20, |
|
||||
), |
|
||||
Divider(), |
|
||||
Container( |
|
||||
margin: EdgeInsets.only( |
|
||||
top: 10, |
|
||||
), |
|
||||
child: Text( |
|
||||
"Teso businesses and other Teso users can only see campaign post on your profile after they have been approved by the campaign owners.", |
|
||||
textAlign: TextAlign.center, |
|
||||
style: TextStyle( |
|
||||
color: Colors.grey, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
floatingActionButton: !sending |
|
||||
? Container( |
|
||||
margin: EdgeInsets.all(20), |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
decoration: BoxDecoration( |
|
||||
borderRadius: BorderRadius.circular(15.0), |
|
||||
color: tesoBlue, |
|
||||
), |
|
||||
child: InkWell( |
|
||||
onTap: () => postVideo(context), |
|
||||
child: Center( |
|
||||
child: Text( |
|
||||
"NEXT", |
|
||||
style: TextStyle( |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
height: 50, |
|
||||
) |
|
||||
: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: 50, |
|
||||
child: Center( |
|
||||
child: CupertinoActivityIndicator( |
|
||||
animating: true, |
|
||||
radius: 15, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,212 +0,0 @@ |
|||||
// import 'package:flutter/material.dart'; |
|
||||
|
|
||||
// import 'package:teso/Classes/TextE.dart'; |
|
||||
// import 'package:teso/util/SizeConfig.dart'; |
|
||||
// import 'textstyler/src/toolbar_action.dart'; |
|
||||
// import 'textstyler/text_style_editor.dart'; |
|
||||
|
|
||||
// // ignore: must_be_immutable |
|
||||
// class TextEdit extends StatefulWidget { |
|
||||
// Textted content; |
|
||||
// TextEdit({Key key, this.content}) : super(key: key); |
|
||||
|
|
||||
// @override |
|
||||
// _TextEditState createState() => _TextEditState(); |
|
||||
// } |
|
||||
|
|
||||
// class _TextEditState extends State<TextEdit> { |
|
||||
// TextStyle textStyle; |
|
||||
// TextAlign textAlign; |
|
||||
// List<String> fonts = [ |
|
||||
// 'Billabong', |
|
||||
// 'AlexBrush', |
|
||||
// 'Allura', |
|
||||
// 'Arizonia', |
|
||||
// 'ChunkFive', |
|
||||
// 'GrandHotel', |
|
||||
// 'GreatVibes', |
|
||||
// 'Lobster', |
|
||||
// 'OpenSans', |
|
||||
// 'OstrichSans', |
|
||||
// 'Oswald', |
|
||||
// 'Pacifico', |
|
||||
// 'Quicksand', |
|
||||
// 'Roboto', |
|
||||
// 'SEASRN', |
|
||||
// 'Windsong', |
|
||||
// ]; |
|
||||
// List<Color> paletteColors = [ |
|
||||
// Colors.black, |
|
||||
// Colors.white, |
|
||||
// Color(int.parse('0xffEA2027')), |
|
||||
// Color(int.parse('0xff006266')), |
|
||||
// Color(int.parse('0xff1B1464')), |
|
||||
// Color(int.parse('0xff5758BB')), |
|
||||
// Color(int.parse('0xff6F1E51')), |
|
||||
// Color(int.parse('0xffB53471')), |
|
||||
// Color(int.parse('0xffEE5A24')), |
|
||||
// Color(int.parse('0xff009432')), |
|
||||
// Color(int.parse('0xff0652DD')), |
|
||||
// Color(int.parse('0xff9980FA')), |
|
||||
// Color(int.parse('0xff833471')), |
|
||||
// Color(int.parse('0xff112CBC4')), |
|
||||
// Color(int.parse('0xffFDA7DF')), |
|
||||
// Color(int.parse('0xffED4C67')), |
|
||||
// Color(int.parse('0xffF79F1F')), |
|
||||
// Color(int.parse('0xffA3CB38')), |
|
||||
// Color(int.parse('0xff1289A7')), |
|
||||
// Color(int.parse('0xffD980FA')) |
|
||||
// ]; |
|
||||
// FocusNode _focus = new FocusNode(); |
|
||||
// TextEditingController controller; |
|
||||
// @override |
|
||||
// void initState() { |
|
||||
// controller = new TextEditingController(); |
|
||||
// textStyle = TextStyle( |
|
||||
// fontSize: 15, |
|
||||
// color: Colors.white, |
|
||||
// fontFamily: 'OpenSans', |
|
||||
// ); |
|
||||
// textAlign = TextAlign.left; |
|
||||
// _focus.addListener(_onFocusChange); |
|
||||
|
|
||||
// controller.text = widget.content.text != null ? widget.content.text : ""; |
|
||||
// textStyle = |
|
||||
// widget.content.textStyle != null ? widget.content.textStyle : null; |
|
||||
// textAlign = widget.content.textAlign != null |
|
||||
// ? widget.content.textAlign |
|
||||
// : TextAlign.center; |
|
||||
// super.initState(); |
|
||||
// } |
|
||||
|
|
||||
// @override |
|
||||
// void dispose() { |
|
||||
// _focus.removeListener(_onFocusChange); |
|
||||
// _focus.dispose(); |
|
||||
// super.dispose(); |
|
||||
// } |
|
||||
|
|
||||
// void _onFocusChange() { |
|
||||
// debugPrint("Focus: " + _focus.hasFocus.toString()); |
|
||||
// } |
|
||||
|
|
||||
// void verify() { |
|
||||
// if (_focus.hasFocus) { |
|
||||
// _focus.unfocus(); |
|
||||
// } else { |
|
||||
// Navigator.pop( |
|
||||
// context, |
|
||||
// new Textted( |
|
||||
// text: controller.text, |
|
||||
// textAlign: textAlign, |
|
||||
// textStyle: textStyle, |
|
||||
// )); |
|
||||
// } |
|
||||
// } |
|
||||
|
|
||||
// @override |
|
||||
// Widget build(BuildContext context) { |
|
||||
// SizeConfig().init(context); |
|
||||
// return Scaffold( |
|
||||
// resizeToAvoidBottomInset: false, |
|
||||
// backgroundColor: Color.fromRGBO(0, 0, 0, 0.8), |
|
||||
// appBar: AppBar( |
|
||||
// backgroundColor: Colors.transparent, |
|
||||
// leading: IconButton( |
|
||||
// onPressed: () => Navigator.pop(context, widget.content), |
|
||||
// icon: Icon( |
|
||||
// Icons.x, |
|
||||
// color: Colors.white, |
|
||||
// ), |
|
||||
// ), |
|
||||
// actions: [ |
|
||||
// IconButton( |
|
||||
// onPressed: verify, |
|
||||
// icon: Icon( |
|
||||
// AntDesign.check, |
|
||||
// color: Colors.white, |
|
||||
// ), |
|
||||
// ), |
|
||||
// ], |
|
||||
// ), |
|
||||
// body: Container( |
|
||||
// height: SizeConfig.safeBlockVertical * 40, |
|
||||
// child: Center( |
|
||||
// child: TextField( |
|
||||
// controller: controller, |
|
||||
// // enabled: false, |
|
||||
// focusNode: _focus, |
|
||||
// style: textStyle, |
|
||||
// textAlign: textAlign, |
|
||||
// // maxLines: 4, |
|
||||
// decoration: new InputDecoration( |
|
||||
// filled: true, |
|
||||
// enabledBorder: OutlineInputBorder( |
|
||||
// borderRadius: BorderRadius.all( |
|
||||
// Radius.circular(10.0), |
|
||||
// ), |
|
||||
// borderSide: BorderSide( |
|
||||
// color: Colors.grey.shade400, |
|
||||
// width: 2, |
|
||||
// ), |
|
||||
// ), |
|
||||
// focusedBorder: OutlineInputBorder( |
|
||||
// borderRadius: BorderRadius.all( |
|
||||
// Radius.circular(10.0), |
|
||||
// ), |
|
||||
// borderSide: BorderSide( |
|
||||
// color: Colors.blue.shade300, |
|
||||
// width: 0, |
|
||||
// ), |
|
||||
// ), |
|
||||
// contentPadding: EdgeInsets.all(15), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// extendBody: false, |
|
||||
// extendBodyBehindAppBar: false, |
|
||||
// bottomSheet: Container( |
|
||||
// height: SizeConfig.safeBlockVertical * 60, |
|
||||
// child: Container( |
|
||||
// padding: EdgeInsets.all(10), |
|
||||
// decoration: BoxDecoration( |
|
||||
// color: Theme.of(context).backgroundColor, |
|
||||
// border: Border.symmetric( |
|
||||
// horizontal: BorderSide( |
|
||||
// color: Theme.of(context).backgroundColor, |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// child: Align( |
|
||||
// alignment: Alignment.topCenter, |
|
||||
// child: SingleChildScrollView( |
|
||||
// scrollDirection: Axis.vertical, |
|
||||
// child: TextStyleEditor( |
|
||||
// fonts: fonts, |
|
||||
// paletteColors: paletteColors, |
|
||||
// textStyle: textStyle, |
|
||||
// textAlign: textAlign, |
|
||||
// initialTool: EditorToolbarAction.fontFamilyTool, |
|
||||
// onTextAlignEdited: (align) { |
|
||||
// setState(() { |
|
||||
// textAlign = align; |
|
||||
// }); |
|
||||
// }, |
|
||||
// onTextStyleEdited: (style) { |
|
||||
// setState(() { |
|
||||
// textStyle = textStyle.merge(style); |
|
||||
// }); |
|
||||
// }, |
|
||||
// onCpasLockTaggle: (caps) { |
|
||||
// print(caps); |
|
||||
// }, |
|
||||
// //onToolbarActionChanged: (fu) => , |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ); |
|
||||
// } |
|
||||
// } |
|
@ -1,560 +0,0 @@ |
|||||
import 'dart:typed_data'; |
|
||||
import 'package:flutter/cupertino.dart'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter/rendering.dart'; |
|
||||
import 'package:flutter/services.dart'; |
|
||||
import 'dart:io'; |
|
||||
|
|
||||
import 'package:image_gallery_saver/image_gallery_saver.dart'; |
|
||||
import 'package:page_transition/page_transition.dart'; |
|
||||
import 'package:share_plus/share_plus.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/file_formats.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trim_editor.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/Trimmer/trimmer.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/Campaign/SubmitAdvert.dart'; |
|
||||
import 'package:teso/util/SizeConfig.dart'; |
|
||||
import 'package:video_player/video_player.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
import 'dart:async'; |
|
||||
import 'package:path_provider/path_provider.dart'; |
|
||||
import 'package:teso/Classes/TesoUser.dart'; |
|
||||
import 'package:provider/provider.dart'; |
|
||||
import 'package:teso/providers/user_provider.dart'; |
|
||||
import 'package:image/image.dart' as IMG; |
|
||||
import 'package:video_thumbnail/video_thumbnail.dart'; |
|
||||
|
|
||||
class VideoReview extends StatefulWidget { |
|
||||
final video; |
|
||||
final bool recorded; |
|
||||
final String campaignID; |
|
||||
final double aspect; |
|
||||
|
|
||||
@override |
|
||||
const VideoReview( |
|
||||
{Key key, |
|
||||
this.video, |
|
||||
@required this.campaignID, |
|
||||
@required this.recorded, |
|
||||
this.aspect}) |
|
||||
: super(key: key); |
|
||||
@override |
|
||||
_VideoReviewState createState() => _VideoReviewState(); |
|
||||
} |
|
||||
|
|
||||
class _VideoReviewState extends State<VideoReview> |
|
||||
with TickerProviderStateMixin { |
|
||||
Trimmer _trimmer = new Trimmer(); |
|
||||
VideoPlayerController videoController; |
|
||||
VoidCallback videoPlayerListener; |
|
||||
bool muted = false; |
|
||||
String readyVideo; |
|
||||
Color textColor = Colors.white; |
|
||||
double _startValue = 0.15; |
|
||||
double _endValue = 60000.0; |
|
||||
var _future; |
|
||||
bool _isPlaying = false; |
|
||||
Duration _duration; |
|
||||
Duration _position; |
|
||||
ByteData bytes; |
|
||||
Uint8List imageBitmap; |
|
||||
Uint8List thumbnail; |
|
||||
Directory tempDirectory; |
|
||||
TesoUser user; |
|
||||
bool processing = false; |
|
||||
bool downloaded = false; |
|
||||
bool processed = false; |
|
||||
final key = new GlobalKey(); |
|
||||
double currentOffset = 0; |
|
||||
|
|
||||
Future<void> _startVideoPlayer() async { |
|
||||
await videoController.play(); |
|
||||
} |
|
||||
|
|
||||
Future<void> initializeController(String fileLocation) async { |
|
||||
videoController = VideoPlayerController.file(File(fileLocation)); |
|
||||
|
|
||||
videoPlayerListener = () async { |
|
||||
Timer.run(() { |
|
||||
this.setState(() { |
|
||||
_position = videoController.value.position; |
|
||||
}); |
|
||||
setState(() { |
|
||||
_duration = Duration(milliseconds: _endValue.round()); |
|
||||
}); |
|
||||
if (_duration?.compareTo(_position) == 0 || |
|
||||
_duration?.compareTo(_position) == -1) { |
|
||||
this.setState(() { |
|
||||
_isPlaying = false; |
|
||||
}); |
|
||||
videoController.pause(); |
|
||||
videoController.seekTo(Duration(milliseconds: _startValue.round())); |
|
||||
} else {} |
|
||||
}); |
|
||||
}; |
|
||||
videoController.addListener(videoPlayerListener); |
|
||||
await videoController.setLooping(true); |
|
||||
await videoController.initialize(); |
|
||||
await _trimmer.loadVideo(videoFile: File(fileLocation)); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
readyVideo = widget.video; |
|
||||
if (readyVideo != null) _future = initializeController(readyVideo); |
|
||||
rootBundle.load("assets/images/rawLogo.png").then((value) => setState(() { |
|
||||
imageBitmap = value.buffer.asUint8List(); |
|
||||
IMG.Image img = IMG.decodeImage(imageBitmap); |
|
||||
IMG.Image resized = IMG.copyResize(img, width: 50, height: 60); |
|
||||
imageBitmap = IMG.encodePng(resized); |
|
||||
})); |
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() { |
|
||||
videoController?.dispose(); |
|
||||
super.dispose(); |
|
||||
} |
|
||||
|
|
||||
void postVideo(context) async { |
|
||||
setState(() { |
|
||||
processing = true; |
|
||||
}); |
|
||||
if (processed) { |
|
||||
await Navigator.pushReplacement( |
|
||||
context, |
|
||||
PageTransition( |
|
||||
type: PageTransitionType.leftToRight, |
|
||||
child: SubmitAdvert( |
|
||||
video: readyVideo, |
|
||||
aspectRatio: widget.recorded |
|
||||
? "0.5625" |
|
||||
: videoController.value.aspectRatio.toString(), |
|
||||
thumbnail: this.thumbnail, |
|
||||
campaignID: widget.campaignID, |
|
||||
), |
|
||||
)); |
|
||||
} else { |
|
||||
readyVideo = await processVideo(context, false); |
|
||||
await Navigator.pushReplacement( |
|
||||
context, |
|
||||
PageTransition( |
|
||||
type: PageTransitionType.leftToRight, |
|
||||
child: SubmitAdvert( |
|
||||
video: readyVideo, |
|
||||
aspectRatio: widget.recorded |
|
||||
? "0.5625" |
|
||||
: videoController.value.aspectRatio.toString(), |
|
||||
thumbnail: this.thumbnail, |
|
||||
campaignID: widget.campaignID, |
|
||||
), |
|
||||
)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Future<void> downloadVideo(context) async { |
|
||||
try { |
|
||||
setState(() { |
|
||||
processing = true; |
|
||||
}); |
|
||||
String output = await processVideo(context, true); |
|
||||
await ImageGallerySaver.saveFile(output).catchError((error, stackTrace) { |
|
||||
setState(() { |
|
||||
processing = false; |
|
||||
downloaded = false; |
|
||||
}); |
|
||||
}).then((value) { |
|
||||
setState(() { |
|
||||
processing = false; |
|
||||
downloaded = true; |
|
||||
}); |
|
||||
}); |
|
||||
} catch (e) { |
|
||||
print(e); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Future<String> processVideo(context, bool watermark) async { |
|
||||
user = Provider.of<UserProvider>(context, listen: false).currentUser; |
|
||||
String location = await getTemporaryDirectory().then((value) => |
|
||||
value.path + |
|
||||
"/" + |
|
||||
DateTime.now().millisecondsSinceEpoch.toString() + |
|
||||
".mp4"); |
|
||||
if (widget.recorded) { |
|
||||
String initial = await _trimmer.saveTrimmedVideo( |
|
||||
applyVideoEncoding: false, |
|
||||
ffmpegCommand: "-vf setsar=1:1 -aspect 9:16", |
|
||||
customVideoFormat: ".mp4", |
|
||||
startValue: _startValue, |
|
||||
endValue: videoController.value.duration.inMilliseconds > 5900 && |
|
||||
videoController.value.duration.inMilliseconds >= _endValue |
|
||||
? _endValue |
|
||||
: double.parse( |
|
||||
videoController.value.duration.inMilliseconds.toString()), |
|
||||
); |
|
||||
this.thumbnail = await generateThumbnail(); |
|
||||
|
|
||||
location = initial; |
|
||||
} else { |
|
||||
String initial = await _trimmer.saveTrimmedVideo( |
|
||||
startValue: _startValue, |
|
||||
endValue: videoController.value.duration.inMilliseconds > 5900 && |
|
||||
videoController.value.duration.inMilliseconds >= _endValue |
|
||||
? _endValue |
|
||||
: double.parse( |
|
||||
videoController.value.duration.inMilliseconds.toString()), |
|
||||
outputFormat: FileFormat.mp4, |
|
||||
); |
|
||||
this.thumbnail = await generateThumbnail(); |
|
||||
|
|
||||
location = initial; |
|
||||
} |
|
||||
return location; |
|
||||
} |
|
||||
|
|
||||
Future<Uint8List> generateThumbnail() async { |
|
||||
try { |
|
||||
Uint8List thumbnail; |
|
||||
|
|
||||
thumbnail = await VideoThumbnail.thumbnailData( |
|
||||
video: widget.video, |
|
||||
imageFormat: ImageFormat.JPEG, |
|
||||
maxWidth: 0, |
|
||||
maxHeight: 0, |
|
||||
timeMs: 100, |
|
||||
quality: 100, |
|
||||
); |
|
||||
return thumbnail; |
|
||||
} catch (e) { |
|
||||
print("Error :::: " + e); |
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Future<void> shareVideo(context) async { |
|
||||
setState(() { |
|
||||
processing = true; |
|
||||
}); |
|
||||
if (readyVideo == widget.video) { |
|
||||
readyVideo = await processVideo(context, true); |
|
||||
Share.shareFiles([readyVideo]); |
|
||||
} else { |
|
||||
Share.shareFiles([readyVideo]); |
|
||||
} |
|
||||
setState(() { |
|
||||
processing = false; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
SizeConfig().init(context); |
|
||||
return Scaffold( |
|
||||
body: FutureBuilder( |
|
||||
future: _future, |
|
||||
builder: (context, snapshot) { |
|
||||
if (snapshot.connectionState == ConnectionState.waiting) { |
|
||||
return Container( |
|
||||
color: Colors.black, |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
child: Center( |
|
||||
child: CircularProgressIndicator( |
|
||||
backgroundColor: Colors.red, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} else { |
|
||||
return Stack( |
|
||||
children: [ |
|
||||
videoContent(context), |
|
||||
// Video trimmer |
|
||||
trimmerWidget(context), |
|
||||
// Pop button |
|
||||
Align( |
|
||||
alignment: Alignment.topLeft, |
|
||||
child: InkWell( |
|
||||
onTap: () => Navigator.pop(context), |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: MediaQuery.of(context).size.width * 0.07, |
|
||||
vertical: MediaQuery.of(context).size.width * 0.1, |
|
||||
), |
|
||||
height: 35, |
|
||||
width: 35, |
|
||||
decoration: BoxDecoration( |
|
||||
color: Color.fromRGBO(0, 0, 0, 0.4), |
|
||||
shape: BoxShape.circle), |
|
||||
child: Icon( |
|
||||
Icons.close, |
|
||||
color: Colors.white, |
|
||||
size: 20, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
// Bottom buttons |
|
||||
bottomButtons(context), |
|
||||
Visibility( |
|
||||
visible: processing, |
|
||||
child: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
color: Color.fromRGBO(0, 0, 0, 0.6), |
|
||||
padding: EdgeInsets.only( |
|
||||
top: MediaQuery.of(context).size.width * 0.7), |
|
||||
child: Center( |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
CupertinoActivityIndicator( |
|
||||
animating: true, |
|
||||
radius: 15, |
|
||||
), |
|
||||
Text( |
|
||||
"Processing.....", |
|
||||
style: TextStyle( |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
); |
|
||||
} |
|
||||
}), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget trimmerWidget(context) { |
|
||||
return Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: MediaQuery.of(context).size.width * 0.01, |
|
||||
vertical: MediaQuery.of(context).size.width * 0.20, |
|
||||
), |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: TrimEditor( |
|
||||
borderPaintColor: tesoGold, |
|
||||
circlePaintColor: tesoBlue, |
|
||||
thumbnailQuality: 100, |
|
||||
showDuration: true, |
|
||||
viewerHeight: 50.0, |
|
||||
maxVideoLength: Duration(seconds: 60), |
|
||||
viewerWidth: MediaQuery.of(context).size.width, |
|
||||
onChangeStart: (value) { |
|
||||
if (!mounted) { |
|
||||
setState(() { |
|
||||
_startValue = value; |
|
||||
}); |
|
||||
} else { |
|
||||
_startValue = value; |
|
||||
} |
|
||||
videoController.seekTo(Duration(milliseconds: value.round())); |
|
||||
}, |
|
||||
onChangeEnd: (value) { |
|
||||
if (!mounted) { |
|
||||
setState(() { |
|
||||
_endValue = value; |
|
||||
}); |
|
||||
} else { |
|
||||
_endValue = value; |
|
||||
} |
|
||||
}, |
|
||||
onChangePlaybackState: (isPlaying) { |
|
||||
if (mounted) |
|
||||
setState(() { |
|
||||
_isPlaying = isPlaying; |
|
||||
}); |
|
||||
}, |
|
||||
)); |
|
||||
} |
|
||||
|
|
||||
Widget videoContent(context) { |
|
||||
return Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
color: Colors.black, |
|
||||
child: Center( |
|
||||
child: AspectRatio( |
|
||||
aspectRatio: videoController.value.size != null |
|
||||
? videoController.value.aspectRatio |
|
||||
: 1.0, |
|
||||
child: Stack( |
|
||||
children: [ |
|
||||
InkWell( |
|
||||
onTap: () { |
|
||||
!_isPlaying ? _startVideoPlayer() : videoController.pause(); |
|
||||
setState(() { |
|
||||
_isPlaying = !_isPlaying; |
|
||||
}); |
|
||||
}, |
|
||||
child: VideoPlayer( |
|
||||
videoController, |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
width: double.infinity, |
|
||||
height: double.infinity, |
|
||||
child: GestureDetector( |
|
||||
child: !_isPlaying |
|
||||
? Icon( |
|
||||
Icons.play_circle, |
|
||||
size: 60, |
|
||||
color: Colors.white, |
|
||||
) |
|
||||
: Container(), |
|
||||
onTap: () { |
|
||||
!_isPlaying |
|
||||
? _startVideoPlayer() |
|
||||
: videoController.pause(); |
|
||||
setState(() { |
|
||||
_isPlaying = !_isPlaying; |
|
||||
}); |
|
||||
}, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
)), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget bottomButtons(context) { |
|
||||
if (widget.recorded) { |
|
||||
return Align( |
|
||||
alignment: Alignment.bottomLeft, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: MediaQuery.of(context).size.width * 0.05, |
|
||||
vertical: SizeConfig.safeBlockVertical * 2.5, |
|
||||
), |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
||||
mainAxisSize: MainAxisSize.min, |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: 55, |
|
||||
height: 40, |
|
||||
padding: EdgeInsets.all(5), |
|
||||
decoration: BoxDecoration( |
|
||||
color: Color.fromRGBO(0, 0, 0, 0.6), |
|
||||
borderRadius: BorderRadius.only( |
|
||||
bottomLeft: Radius.circular(30), |
|
||||
bottomRight: Radius.circular(30), |
|
||||
topRight: Radius.circular(30), |
|
||||
topLeft: Radius.circular(30), |
|
||||
), |
|
||||
border: Border.all(color: Colors.white, width: 0.5)), |
|
||||
child: InkWell( |
|
||||
onTap: () async => |
|
||||
!downloaded ? await downloadVideo(context) : null, |
|
||||
child: Icon( |
|
||||
!downloaded ? Icons.download : Icons.check, |
|
||||
color: !downloaded ? Colors.white : Colors.green, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
width: 55, |
|
||||
height: 40, |
|
||||
padding: EdgeInsets.all(5), |
|
||||
decoration: BoxDecoration( |
|
||||
color: Color.fromRGBO(0, 0, 0, 0.6), |
|
||||
borderRadius: BorderRadius.only( |
|
||||
bottomLeft: Radius.circular(30), |
|
||||
bottomRight: Radius.circular(30), |
|
||||
topRight: Radius.circular(30), |
|
||||
topLeft: Radius.circular(30), |
|
||||
), |
|
||||
border: Border.all(color: Colors.white, width: 0.5)), |
|
||||
child: InkWell( |
|
||||
onTap: () async => await shareVideo(context), |
|
||||
child: Icon( |
|
||||
Icons.share, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
padding: EdgeInsets.all(5), |
|
||||
width: 100, |
|
||||
height: 40, |
|
||||
decoration: BoxDecoration( |
|
||||
color: tesoGold, |
|
||||
borderRadius: BorderRadius.only( |
|
||||
bottomLeft: Radius.circular(30), |
|
||||
bottomRight: Radius.circular(30), |
|
||||
topRight: Radius.circular(30), |
|
||||
topLeft: Radius.circular(30), |
|
||||
), |
|
||||
), |
|
||||
child: InkWell( |
|
||||
onTap: () => postVideo(context), |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
|
||||
children: [ |
|
||||
Text( |
|
||||
"Post", |
|
||||
style: TextStyle( |
|
||||
fontWeight: FontWeight.bold, |
|
||||
), |
|
||||
), |
|
||||
Icon( |
|
||||
Icons.send, |
|
||||
color: tesoBlue, |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} else { |
|
||||
return Align( |
|
||||
alignment: Alignment.bottomRight, |
|
||||
child: Container( |
|
||||
padding: EdgeInsets.all(5), |
|
||||
width: 100, |
|
||||
height: 40, |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
vertical: 10, |
|
||||
horizontal: 20, |
|
||||
), |
|
||||
decoration: BoxDecoration( |
|
||||
color: tesoGold, |
|
||||
borderRadius: BorderRadius.only( |
|
||||
bottomLeft: Radius.circular(30), |
|
||||
bottomRight: Radius.circular(30), |
|
||||
topRight: Radius.circular(30), |
|
||||
topLeft: Radius.circular(30), |
|
||||
), |
|
||||
), |
|
||||
child: InkWell( |
|
||||
onTap: () => postVideo(context), |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
|
||||
children: [ |
|
||||
Text( |
|
||||
"Post", |
|
||||
style: TextStyle( |
|
||||
fontWeight: FontWeight.bold, |
|
||||
), |
|
||||
), |
|
||||
Icon( |
|
||||
Icons.send, |
|
||||
color: tesoBlue, |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,92 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
class ColorPalette extends StatefulWidget { |
|
||||
final Color activeColor; |
|
||||
final List<Color> colors; |
|
||||
final Function(Color) onColorPicked; |
|
||||
|
|
||||
ColorPalette({ |
|
||||
this.activeColor, |
|
||||
this.onColorPicked, |
|
||||
this.colors, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
_ColorPaletteState createState() => _ColorPaletteState(); |
|
||||
} |
|
||||
|
|
||||
class _ColorPaletteState extends State<ColorPalette> { |
|
||||
Color _activeColor; |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
_activeColor = widget.activeColor ?? widget.colors[0]; |
|
||||
|
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Padding( |
|
||||
padding: EdgeInsets.all(16), |
|
||||
child: Wrap( |
|
||||
spacing: 16, |
|
||||
runSpacing: 16, |
|
||||
children: widget.colors |
|
||||
.map( |
|
||||
(color) => _ColorHolder( |
|
||||
color: color, |
|
||||
active: color == _activeColor, |
|
||||
onTap: (color) { |
|
||||
setState(() => _activeColor = color); |
|
||||
widget.onColorPicked(color); |
|
||||
}, |
|
||||
), |
|
||||
) |
|
||||
.toList(), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class _ColorHolder extends StatelessWidget { |
|
||||
final Color color; |
|
||||
final Function(Color) onTap; |
|
||||
final bool active; |
|
||||
|
|
||||
_ColorHolder({ |
|
||||
this.color, |
|
||||
this.onTap, |
|
||||
this.active = false, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Container( |
|
||||
height: 40, |
|
||||
width: 40, |
|
||||
decoration: BoxDecoration( |
|
||||
border: active |
|
||||
? Border.fromBorderSide( |
|
||||
BorderSide(color: Theme.of(context).colorScheme.onSurface)) |
|
||||
: null, |
|
||||
borderRadius: BorderRadius.circular(50), |
|
||||
), |
|
||||
child: Center( |
|
||||
child: GestureDetector( |
|
||||
onTap: () => onTap(color), |
|
||||
child: Container( |
|
||||
height: 35, |
|
||||
width: 35, |
|
||||
decoration: BoxDecoration( |
|
||||
border: Border.fromBorderSide( |
|
||||
BorderSide(color: Theme.of(context).colorScheme.onSurface)), |
|
||||
borderRadius: BorderRadius.circular(50), |
|
||||
color: color, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,30 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
class OptionButton extends StatelessWidget { |
|
||||
final bool isActive; |
|
||||
final Function() onPressed; |
|
||||
final Widget child; |
|
||||
final Size size; |
|
||||
|
|
||||
OptionButton({ |
|
||||
this.onPressed, |
|
||||
this.child, |
|
||||
this.isActive = false, |
|
||||
this.size, |
|
||||
}); |
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return RawMaterialButton( |
|
||||
constraints: BoxConstraints.tight(size ?? Size(45, 45)), |
|
||||
highlightColor: Theme.of(context).colorScheme.background, |
|
||||
splashColor: Theme.of(context).colorScheme.background, |
|
||||
fillColor: isActive ? Theme.of(context).colorScheme.background : null, |
|
||||
shape: RoundedRectangleBorder( |
|
||||
borderRadius: BorderRadius.circular(12), |
|
||||
side: BorderSide(color: Theme.of(context).colorScheme.surface), |
|
||||
), |
|
||||
child: child, |
|
||||
onPressed: onPressed, |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,87 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
import 'option_button.dart'; |
|
||||
import 'toolbar_action.dart'; |
|
||||
|
|
||||
class Toolbar extends StatefulWidget { |
|
||||
final EditorToolbarAction initialTool; |
|
||||
final Function(EditorToolbarAction) onToolSelect; |
|
||||
|
|
||||
Toolbar({ |
|
||||
this.initialTool = EditorToolbarAction.editor, |
|
||||
this.onToolSelect, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
_ToolbarState createState() => _ToolbarState(); |
|
||||
} |
|
||||
|
|
||||
class _ToolbarState extends State<Toolbar> { |
|
||||
EditorToolbarAction _selectedAction; |
|
||||
@override |
|
||||
void initState() { |
|
||||
_selectedAction = widget.initialTool; |
|
||||
|
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
|
||||
children: [ |
|
||||
// OptionButton( |
|
||||
// isActive: _selectedAction == EditorToolbarAction.editor, |
|
||||
// child: Icon(Icons.keyboard), |
|
||||
// onPressed: () { |
|
||||
// setState(() => _selectedAction = EditorToolbarAction.editor); |
|
||||
// widget.onToolSelect(EditorToolbarAction.editor); |
|
||||
// }, |
|
||||
// ), |
|
||||
OptionButton( |
|
||||
isActive: _selectedAction == EditorToolbarAction.fontFamilyTool, |
|
||||
child: Icon(Icons.title), |
|
||||
onPressed: () { |
|
||||
setState( |
|
||||
() => _selectedAction = EditorToolbarAction.fontFamilyTool); |
|
||||
widget.onToolSelect(EditorToolbarAction.fontFamilyTool); |
|
||||
}, |
|
||||
), |
|
||||
OptionButton( |
|
||||
isActive: _selectedAction == EditorToolbarAction.fontOptionTool, |
|
||||
child: Icon(Icons.strikethrough_s), |
|
||||
onPressed: () { |
|
||||
setState( |
|
||||
() => _selectedAction = EditorToolbarAction.fontOptionTool); |
|
||||
widget.onToolSelect(EditorToolbarAction.fontOptionTool); |
|
||||
}, |
|
||||
), |
|
||||
OptionButton( |
|
||||
isActive: _selectedAction == EditorToolbarAction.fontSizeTool, |
|
||||
child: Icon(Icons.format_size), |
|
||||
onPressed: () { |
|
||||
setState(() => _selectedAction = EditorToolbarAction.fontSizeTool); |
|
||||
widget.onToolSelect(EditorToolbarAction.fontSizeTool); |
|
||||
}, |
|
||||
), |
|
||||
OptionButton( |
|
||||
isActive: _selectedAction == EditorToolbarAction.fontColorTool, |
|
||||
child: Icon(Icons.format_color_text), |
|
||||
onPressed: () { |
|
||||
setState(() => _selectedAction = EditorToolbarAction.fontColorTool); |
|
||||
widget.onToolSelect(EditorToolbarAction.fontColorTool); |
|
||||
}, |
|
||||
), |
|
||||
OptionButton( |
|
||||
isActive: _selectedAction == EditorToolbarAction.backgroundColorTool, |
|
||||
child: Icon(Icons.format_color_fill), |
|
||||
onPressed: () { |
|
||||
setState(() => |
|
||||
_selectedAction = EditorToolbarAction.backgroundColorTool); |
|
||||
widget.onToolSelect(EditorToolbarAction.backgroundColorTool); |
|
||||
}, |
|
||||
), |
|
||||
], |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,8 +0,0 @@ |
|||||
enum EditorToolbarAction { |
|
||||
editor, |
|
||||
fontFamilyTool, |
|
||||
fontOptionTool, |
|
||||
fontSizeTool, |
|
||||
fontColorTool, |
|
||||
backgroundColorTool, |
|
||||
} |
|
@ -1,24 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
import '../color_palette.dart'; |
|
||||
|
|
||||
class BackgroundColorTool extends StatelessWidget { |
|
||||
final List<Color> colors; |
|
||||
final Color activeColor; |
|
||||
final Function(Color) onColorPicked; |
|
||||
|
|
||||
BackgroundColorTool({ |
|
||||
this.colors, |
|
||||
this.onColorPicked, |
|
||||
this.activeColor, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return ColorPalette( |
|
||||
activeColor: activeColor, |
|
||||
onColorPicked: onColorPicked, |
|
||||
colors: colors, |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,24 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
import '../color_palette.dart'; |
|
||||
|
|
||||
class FontColorTool extends StatelessWidget { |
|
||||
final List<Color> colors; |
|
||||
final Color activeColor; |
|
||||
final Function(Color) onColorPicked; |
|
||||
|
|
||||
FontColorTool({ |
|
||||
this.colors, |
|
||||
this.onColorPicked, |
|
||||
this.activeColor, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return ColorPalette( |
|
||||
activeColor: activeColor, |
|
||||
onColorPicked: onColorPicked, |
|
||||
colors: colors, |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,66 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
import '../option_button.dart'; |
|
||||
|
|
||||
class FontFamilyTool extends StatefulWidget { |
|
||||
final List<String> fonts; |
|
||||
final Function(String) onSelectFont; |
|
||||
final String selectedFont; |
|
||||
|
|
||||
FontFamilyTool({ |
|
||||
this.fonts, |
|
||||
this.onSelectFont, |
|
||||
this.selectedFont, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
_FontFamilyToolState createState() => _FontFamilyToolState(); |
|
||||
} |
|
||||
|
|
||||
class _FontFamilyToolState extends State<FontFamilyTool> { |
|
||||
String _selectedFont; |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
_selectedFont = widget.selectedFont ?? widget.fonts[0]; |
|
||||
|
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Wrap( |
|
||||
spacing: 12, |
|
||||
runSpacing: 12, |
|
||||
children: widget.fonts |
|
||||
.map<_FontFamily>( |
|
||||
(font) => _FontFamily( |
|
||||
font, |
|
||||
isSelected: _selectedFont == font, |
|
||||
onSelect: (selectedFont) { |
|
||||
setState(() => _selectedFont = selectedFont); |
|
||||
widget.onSelectFont(selectedFont); |
|
||||
}, |
|
||||
), |
|
||||
) |
|
||||
.toList(), |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class _FontFamily extends StatelessWidget { |
|
||||
final String font; |
|
||||
final bool isSelected; |
|
||||
final Function(String) onSelect; |
|
||||
|
|
||||
_FontFamily(this.font, {this.onSelect, this.isSelected = false}); |
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return OptionButton( |
|
||||
isActive: isSelected, |
|
||||
size: Size(90, 45), |
|
||||
onPressed: () => onSelect(font), |
|
||||
child: Center(child: Text(font, style: TextStyle(fontFamily: font))), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,123 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
class FontSizeTool extends StatelessWidget { |
|
||||
final double fontSize; |
|
||||
final double letterSpacing; |
|
||||
final double letterHeight; |
|
||||
final Function( |
|
||||
double fontSize, |
|
||||
double letterSpacing, |
|
||||
double letterHeight, |
|
||||
) onFontSizeEdited; |
|
||||
|
|
||||
FontSizeTool({ |
|
||||
this.onFontSizeEdited, |
|
||||
this.fontSize = 0, |
|
||||
this.letterSpacing = 0, |
|
||||
this.letterHeight = 0, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
double _fontSize = fontSize; |
|
||||
double _letterSpacing = letterSpacing; |
|
||||
double _letterHeight = letterHeight; |
|
||||
|
|
||||
return Padding( |
|
||||
padding: EdgeInsets.all(16), |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
_ResizeSlider( |
|
||||
value: _fontSize, |
|
||||
max: 45, |
|
||||
icon: Icons.format_size, |
|
||||
onChange: (value) { |
|
||||
_fontSize = value; |
|
||||
onFontSizeEdited(_fontSize, _letterSpacing, _letterHeight); |
|
||||
}, |
|
||||
), |
|
||||
_ResizeSlider( |
|
||||
value: _letterHeight, |
|
||||
icon: Icons.format_line_spacing, |
|
||||
max: 10, |
|
||||
onChange: (value) { |
|
||||
_letterHeight = value; |
|
||||
onFontSizeEdited(_fontSize, _letterSpacing, _letterHeight); |
|
||||
}, |
|
||||
), |
|
||||
_ResizeSlider( |
|
||||
value: _letterSpacing, |
|
||||
icon: Icons.settings_ethernet, |
|
||||
max: 10, |
|
||||
onChange: (value) { |
|
||||
_letterSpacing = value; |
|
||||
onFontSizeEdited(_fontSize, _letterSpacing, _letterHeight); |
|
||||
}, |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class _ResizeSlider extends StatefulWidget { |
|
||||
final double value; |
|
||||
final double min; |
|
||||
final double max; |
|
||||
final IconData icon; |
|
||||
final Function(double) onChange; |
|
||||
|
|
||||
_ResizeSlider({ |
|
||||
this.value, |
|
||||
this.icon, |
|
||||
this.onChange, |
|
||||
this.min = 0, |
|
||||
this.max = 100, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
_ResizeSliderState createState() => _ResizeSliderState(); |
|
||||
} |
|
||||
|
|
||||
class _ResizeSliderState extends State<_ResizeSlider> { |
|
||||
double _value; |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
_value = widget.value; |
|
||||
|
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Row( |
|
||||
children: [ |
|
||||
Icon(widget.icon), |
|
||||
Expanded( |
|
||||
child: SliderTheme( |
|
||||
data: SliderThemeData( |
|
||||
activeTrackColor: Theme.of(context).colorScheme.background, |
|
||||
inactiveTrackColor: Theme.of(context).colorScheme.background, |
|
||||
thumbColor: Theme.of(context).colorScheme.background, |
|
||||
overlayColor: |
|
||||
Theme.of(context).colorScheme.background.withOpacity(0.2), |
|
||||
trackHeight: 2, |
|
||||
), |
|
||||
child: Slider( |
|
||||
value: _value, |
|
||||
onChanged: (value) { |
|
||||
setState(() => _value = value); |
|
||||
|
|
||||
widget.onChange(value); |
|
||||
}, |
|
||||
min: widget.min, |
|
||||
max: widget.max, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Text(_value.toStringAsFixed(1)), |
|
||||
], |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,237 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
import '../option_button.dart'; |
|
||||
|
|
||||
class TextFormatTool extends StatelessWidget { |
|
||||
final Function( |
|
||||
bool bold, |
|
||||
bool italic, |
|
||||
) onTextFormatEdited; |
|
||||
final Function(bool caps) onCpasLockTaggle; |
|
||||
final Function(TextAlign textAlign) onTextAlignEdited; |
|
||||
final TextAlign textAlign; |
|
||||
final bool bold; |
|
||||
final bool italic; |
|
||||
final bool caps; |
|
||||
|
|
||||
TextFormatTool({ |
|
||||
this.onTextFormatEdited, |
|
||||
this.onTextAlignEdited, |
|
||||
this.onCpasLockTaggle, |
|
||||
this.textAlign = TextAlign.left, |
|
||||
this.bold = false, |
|
||||
this.italic = false, |
|
||||
this.caps = false, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Container( |
|
||||
margin: EdgeInsets.only(top: 36), |
|
||||
child: Column( |
|
||||
crossAxisAlignment: CrossAxisAlignment.center, |
|
||||
children: [ |
|
||||
_TextFormatEditor( |
|
||||
bold: bold, |
|
||||
italic: italic, |
|
||||
caps: caps, |
|
||||
onFormatEdited: onTextFormatEdited, |
|
||||
onCpasLockTaggle: onCpasLockTaggle, |
|
||||
), |
|
||||
SizedBox(height: 36), |
|
||||
_TextAlignEditor( |
|
||||
textAlign: textAlign, |
|
||||
onTextAlignEdited: onTextAlignEdited, |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class _TextAlignEditor extends StatefulWidget { |
|
||||
final TextAlign textAlign; |
|
||||
final Function(TextAlign textAlign) onTextAlignEdited; |
|
||||
|
|
||||
_TextAlignEditor({ |
|
||||
this.onTextAlignEdited, |
|
||||
this.textAlign = TextAlign.left, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
_TextAlignEditorState createState() => _TextAlignEditorState(); |
|
||||
} |
|
||||
|
|
||||
class _TextAlignEditorState extends State<_TextAlignEditor> { |
|
||||
TextAlign _textAlign; |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
_textAlign = widget.textAlign; |
|
||||
|
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
|
||||
children: [ |
|
||||
_TextAlignOption( |
|
||||
icon: Icons.format_align_left, |
|
||||
isActive: _textAlign == TextAlign.left, |
|
||||
onPressed: () { |
|
||||
setState(() => _textAlign = TextAlign.left); |
|
||||
widget.onTextAlignEdited(_textAlign); |
|
||||
}, |
|
||||
), |
|
||||
_TextAlignOption( |
|
||||
icon: Icons.format_align_center, |
|
||||
isActive: _textAlign == TextAlign.center, |
|
||||
onPressed: () { |
|
||||
setState(() => _textAlign = TextAlign.center); |
|
||||
widget.onTextAlignEdited(_textAlign); |
|
||||
}, |
|
||||
), |
|
||||
_TextAlignOption( |
|
||||
icon: Icons.format_align_right, |
|
||||
isActive: _textAlign == TextAlign.right, |
|
||||
onPressed: () { |
|
||||
setState(() => _textAlign = TextAlign.right); |
|
||||
widget.onTextAlignEdited(_textAlign); |
|
||||
}, |
|
||||
), |
|
||||
_TextAlignOption( |
|
||||
icon: Icons.format_align_justify, |
|
||||
isActive: _textAlign == TextAlign.justify, |
|
||||
onPressed: () { |
|
||||
setState(() => _textAlign = TextAlign.justify); |
|
||||
widget.onTextAlignEdited(_textAlign); |
|
||||
}, |
|
||||
), |
|
||||
], |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class _TextAlignOption extends StatelessWidget { |
|
||||
final IconData icon; |
|
||||
final Function() onPressed; |
|
||||
final bool isActive; |
|
||||
|
|
||||
_TextAlignOption({ |
|
||||
this.icon, |
|
||||
this.onPressed, |
|
||||
this.isActive = false, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return IconButton( |
|
||||
iconSize: 32, |
|
||||
icon: Icon(icon), |
|
||||
color: isActive |
|
||||
? Theme.of(context).iconTheme.color |
|
||||
: Theme.of(context).disabledColor, |
|
||||
onPressed: onPressed, |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class _TextFormatEditor extends StatefulWidget { |
|
||||
final Function(bool bold, bool italic) onFormatEdited; |
|
||||
final Function(bool caps) onCpasLockTaggle; |
|
||||
final bool bold; |
|
||||
final bool italic; |
|
||||
final bool caps; |
|
||||
|
|
||||
_TextFormatEditor({ |
|
||||
this.onFormatEdited, |
|
||||
this.onCpasLockTaggle, |
|
||||
this.bold = false, |
|
||||
this.italic = false, |
|
||||
this.caps = false, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
_TextFormatEditorState createState() => _TextFormatEditorState(); |
|
||||
} |
|
||||
|
|
||||
class _TextFormatEditorState extends State<_TextFormatEditor> { |
|
||||
bool _bold; |
|
||||
bool _italic; |
|
||||
bool _caps; |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
_bold = widget.bold; |
|
||||
_italic = widget.italic; |
|
||||
_caps = widget.caps; |
|
||||
|
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
|
||||
children: [ |
|
||||
_TextFormatOption( |
|
||||
title: 'BOLD', |
|
||||
icon: Icons.format_bold, |
|
||||
isActive: _bold, |
|
||||
onPressed: () { |
|
||||
setState(() => _bold = !_bold); |
|
||||
widget.onFormatEdited(_bold, _italic); |
|
||||
}, |
|
||||
), |
|
||||
_TextFormatOption( |
|
||||
title: 'ITALIC', |
|
||||
icon: Icons.format_italic, |
|
||||
isActive: _italic, |
|
||||
onPressed: () { |
|
||||
setState(() => _italic = !_italic); |
|
||||
widget.onFormatEdited(_bold, _italic); |
|
||||
}, |
|
||||
), |
|
||||
_TextFormatOption( |
|
||||
title: 'CAPS', |
|
||||
icon: Icons.keyboard_capslock, |
|
||||
isActive: _caps, |
|
||||
onPressed: () { |
|
||||
setState(() => _caps = !_caps); |
|
||||
widget.onCpasLockTaggle(_caps); |
|
||||
}, |
|
||||
), |
|
||||
], |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
class _TextFormatOption extends StatelessWidget { |
|
||||
final String title; |
|
||||
final IconData icon; |
|
||||
final Function() onPressed; |
|
||||
final bool isActive; |
|
||||
|
|
||||
_TextFormatOption({ |
|
||||
this.title, |
|
||||
this.icon, |
|
||||
this.onPressed, |
|
||||
this.isActive = false, |
|
||||
}); |
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Column( |
|
||||
children: [ |
|
||||
OptionButton( |
|
||||
isActive: isActive, |
|
||||
onPressed: onPressed, |
|
||||
child: Icon(icon), |
|
||||
), |
|
||||
SizedBox(height: 12), |
|
||||
Text(title), |
|
||||
], |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,226 +0,0 @@ |
|||||
library text_style_editor; |
|
||||
|
|
||||
export 'src/toolbar_action.dart'; |
|
||||
|
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'src/toolbar_action.dart'; |
|
||||
import 'src/tools/background_color_tool.dart'; |
|
||||
import 'src/color_palette.dart'; |
|
||||
import 'src/tools/font_family_tool.dart'; |
|
||||
import 'src/tools/font_size_tool.dart'; |
|
||||
import 'src/tools/text_format_tool.dart'; |
|
||||
import 'src/toolbar.dart'; |
|
||||
|
|
||||
/// Text style editor |
|
||||
/// A flutter widget that edit text style and text alignment |
|
||||
/// |
|
||||
/// You can pass your text style or alignment to the widget |
|
||||
/// and then get the edited text style |
|
||||
class TextStyleEditor extends StatefulWidget { |
|
||||
/// Editor's font families |
|
||||
final List<String> fonts; |
|
||||
|
|
||||
/// The text style |
|
||||
final TextStyle textStyle; |
|
||||
|
|
||||
/// The text alignment |
|
||||
final TextAlign textAlign; |
|
||||
|
|
||||
/// The inithial editor tool |
|
||||
final EditorToolbarAction initialTool; |
|
||||
|
|
||||
/// Editor's palette colors |
|
||||
final List<Color> paletteColors; |
|
||||
|
|
||||
/// [onTextStyleEdited] will be called after [textStyle] prop has changed |
|
||||
final Function(TextStyle) onTextStyleEdited; |
|
||||
|
|
||||
/// [onTextAlignEdited] will be called after [textAlingment] prop has changed |
|
||||
final Function(TextAlign) onTextAlignEdited; |
|
||||
|
|
||||
/// [onCpasLockTaggle] will be called after caps lock has changed |
|
||||
final Function(bool) onCpasLockTaggle; |
|
||||
|
|
||||
/// [onToolbarActionChanged] will be called after editor's tool has changed |
|
||||
final Function(EditorToolbarAction) onToolbarActionChanged; |
|
||||
|
|
||||
/// Create a [TextStyleEditor] widget |
|
||||
/// |
|
||||
/// [fonts] list of font families that you want to use in editor. |
|
||||
/// [textStyle] initiate text style. |
|
||||
/// [textAlign] initiate text alignment. |
|
||||
/// |
|
||||
/// [onTextStyleEdited] callback will be called every time [textStyle] has changed. |
|
||||
/// [onTextAlignEdited] callback will be called every time [textAlign] has changed. |
|
||||
/// [onCpasLockTaggle] callback will be called every time caps lock has changed to off or on. |
|
||||
/// [onToolbarActionChanged] callback will be called every time editor's tool has changed. |
|
||||
TextStyleEditor({ |
|
||||
this.fonts, |
|
||||
this.textStyle, |
|
||||
this.textAlign, |
|
||||
this.paletteColors, |
|
||||
this.initialTool = EditorToolbarAction.editor, |
|
||||
this.onTextStyleEdited, |
|
||||
this.onTextAlignEdited, |
|
||||
this.onCpasLockTaggle, |
|
||||
this.onToolbarActionChanged, |
|
||||
}); |
|
||||
|
|
||||
@override |
|
||||
_TextStyleEditorState createState() => _TextStyleEditorState(); |
|
||||
} |
|
||||
|
|
||||
class _TextStyleEditorState extends State<TextStyleEditor> { |
|
||||
EditorToolbarAction _currentTool; |
|
||||
TextStyle _textStyle; |
|
||||
TextAlign _textAlign; |
|
||||
List<Color> _paletteColors; |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
_currentTool = widget.initialTool; |
|
||||
_textStyle = widget.textStyle; |
|
||||
_textAlign = widget.textAlign; |
|
||||
|
|
||||
// Set default palette's colors |
|
||||
_paletteColors = widget.paletteColors ?? |
|
||||
[ |
|
||||
Colors.black, |
|
||||
Colors.white, |
|
||||
Colors.red, |
|
||||
Colors.blue, |
|
||||
Colors.blueAccent, |
|
||||
Colors.brown, |
|
||||
Colors.green, |
|
||||
Colors.indigoAccent, |
|
||||
Colors.lime, |
|
||||
]; |
|
||||
|
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Container( |
|
||||
color: Theme.of(context).backgroundColor, |
|
||||
child: Column( |
|
||||
mainAxisSize: MainAxisSize.min, |
|
||||
children: [ |
|
||||
Toolbar( |
|
||||
initialTool: _currentTool, |
|
||||
onToolSelect: (action) { |
|
||||
setState(() => _currentTool = action); |
|
||||
if (widget.onToolbarActionChanged != null) { |
|
||||
widget.onToolbarActionChanged(action); |
|
||||
} |
|
||||
}, |
|
||||
), |
|
||||
Divider(), |
|
||||
Container( |
|
||||
child: SingleChildScrollView( |
|
||||
child: () { |
|
||||
// Choice tools |
|
||||
switch (_currentTool) { |
|
||||
case EditorToolbarAction.fontFamilyTool: |
|
||||
return FontFamilyTool( |
|
||||
fonts: widget.fonts, |
|
||||
selectedFont: _textStyle.fontFamily, |
|
||||
onSelectFont: (fontFamily) { |
|
||||
setState(() => _textStyle = |
|
||||
_textStyle.copyWith(fontFamily: fontFamily)); |
|
||||
|
|
||||
if (widget.onTextStyleEdited != null) { |
|
||||
widget.onTextStyleEdited(_textStyle); |
|
||||
} |
|
||||
}, |
|
||||
); |
|
||||
case EditorToolbarAction.fontOptionTool: |
|
||||
return TextFormatTool( |
|
||||
bold: _textStyle.fontWeight == FontWeight.bold, |
|
||||
italic: _textStyle.fontStyle == FontStyle.italic, |
|
||||
textAlign: _textAlign, |
|
||||
onTextFormatEdited: (bold, italic) { |
|
||||
setState(() => _textStyle = _textStyle.copyWith( |
|
||||
fontWeight: |
|
||||
bold ? FontWeight.bold : FontWeight.normal, |
|
||||
fontStyle: |
|
||||
italic ? FontStyle.italic : FontStyle.normal, |
|
||||
)); |
|
||||
|
|
||||
if (widget.onTextStyleEdited != null) { |
|
||||
widget.onTextStyleEdited(_textStyle); |
|
||||
} |
|
||||
}, |
|
||||
onTextAlignEdited: (align) { |
|
||||
setState(() => _textAlign = align); |
|
||||
|
|
||||
if (widget.onTextAlignEdited != null) { |
|
||||
widget.onTextAlignEdited(align); |
|
||||
} |
|
||||
}, |
|
||||
onCpasLockTaggle: (caps) { |
|
||||
if (widget.onCpasLockTaggle != null) { |
|
||||
widget.onCpasLockTaggle(caps); |
|
||||
} |
|
||||
}, |
|
||||
); |
|
||||
case EditorToolbarAction.fontSizeTool: |
|
||||
return FontSizeTool( |
|
||||
fontSize: _textStyle.fontSize ?? 0, |
|
||||
letterHeight: _textStyle.height ?? 1.2, |
|
||||
letterSpacing: _textStyle.letterSpacing ?? 1, |
|
||||
onFontSizeEdited: ( |
|
||||
fontSize, |
|
||||
letterSpacing, |
|
||||
letterHeight, |
|
||||
) { |
|
||||
setState(() => _textStyle = _textStyle.copyWith( |
|
||||
fontSize: fontSize, |
|
||||
height: letterHeight, |
|
||||
letterSpacing: letterSpacing, |
|
||||
)); |
|
||||
|
|
||||
if (widget.onTextStyleEdited != null) { |
|
||||
widget.onTextStyleEdited(_textStyle); |
|
||||
} |
|
||||
}, |
|
||||
); |
|
||||
case EditorToolbarAction.fontColorTool: |
|
||||
return BackgroundColorTool( |
|
||||
activeColor: _textStyle.color, |
|
||||
colors: _paletteColors, |
|
||||
onColorPicked: (color) { |
|
||||
setState(() => |
|
||||
_textStyle = _textStyle.copyWith(color: color)); |
|
||||
|
|
||||
if (widget.onTextStyleEdited != null) { |
|
||||
widget.onTextStyleEdited(_textStyle); |
|
||||
} |
|
||||
}, |
|
||||
); |
|
||||
case EditorToolbarAction.backgroundColorTool: |
|
||||
return ColorPalette( |
|
||||
activeColor: _textStyle.backgroundColor, |
|
||||
colors: _paletteColors, |
|
||||
onColorPicked: (color) { |
|
||||
setState(() => _textStyle = |
|
||||
_textStyle.copyWith(backgroundColor: color)); |
|
||||
|
|
||||
if (widget.onTextStyleEdited != null) { |
|
||||
widget.onTextStyleEdited(_textStyle); |
|
||||
} |
|
||||
}, |
|
||||
); |
|
||||
case EditorToolbarAction.editor: |
|
||||
return Container(); |
|
||||
default: |
|
||||
return Container(); |
|
||||
} |
|
||||
}(), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,500 +0,0 @@ |
|||||
import 'dart:typed_data'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:camera/camera.dart'; |
|
||||
import 'package:flutter/services.dart'; |
|
||||
|
|
||||
import 'package:image_picker/image_picker.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
import 'package:page_transition/page_transition.dart'; |
|
||||
import 'dart:async'; |
|
||||
import 'package:circular_countdown_timer/circular_countdown_timer.dart'; |
|
||||
import 'package:video_thumbnail/video_thumbnail.dart' as thumb; |
|
||||
|
|
||||
import 'Editor/VideoReview.dart'; |
|
||||
|
|
||||
// ignore: must_be_immutable |
|
||||
class RecordVideo extends StatefulWidget { |
|
||||
final String campaignID; |
|
||||
List<CameraDescription> connectedCameras; |
|
||||
|
|
||||
RecordVideo({Key key, this.connectedCameras, @required this.campaignID}) |
|
||||
: super(key: key); |
|
||||
@override |
|
||||
_RecordVideoState createState() => _RecordVideoState(); |
|
||||
} |
|
||||
|
|
||||
class _RecordVideoState extends State<RecordVideo> |
|
||||
with TickerProviderStateMixin { |
|
||||
CameraController _controller; |
|
||||
int selectedCamera = 0; |
|
||||
bool flash = false; |
|
||||
bool frontFlash = false; |
|
||||
bool recording = false; |
|
||||
AnimationController _recordingAnimationController; |
|
||||
XFile video; |
|
||||
String filePath; |
|
||||
int recordEnd = 60; |
|
||||
CountDownController _controllerCountDown = CountDownController(); |
|
||||
final interval = const Duration(seconds: 1); |
|
||||
final picker = ImagePicker(); |
|
||||
bool gallery = false; |
|
||||
|
|
||||
final int timerMaxSeconds = 60; |
|
||||
|
|
||||
int currentSeconds = 0; |
|
||||
|
|
||||
flipCamera() { |
|
||||
selectedCamera++; |
|
||||
if (selectedCamera < widget.connectedCameras.length) { |
|
||||
onNewCameraSelected(widget.connectedCameras.elementAt(selectedCamera)); |
|
||||
} else { |
|
||||
selectedCamera = 0; |
|
||||
onNewCameraSelected(widget.connectedCameras.elementAt(selectedCamera)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
flashCamera() { |
|
||||
try { |
|
||||
if (!flash && |
|
||||
_controller.description.lensDirection == CameraLensDirection.back) { |
|
||||
_controller.setFlashMode(FlashMode.torch); |
|
||||
setState(() { |
|
||||
flash = true; |
|
||||
frontFlash = false; |
|
||||
}); |
|
||||
} else if (!flash && |
|
||||
_controller.description.lensDirection == CameraLensDirection.front) { |
|
||||
setState(() { |
|
||||
flash = true; |
|
||||
frontFlash = true; |
|
||||
}); |
|
||||
} else if (flash && |
|
||||
_controller.description.lensDirection == CameraLensDirection.back) { |
|
||||
_controller.setFlashMode(FlashMode.off); |
|
||||
setState(() { |
|
||||
flash = false; |
|
||||
}); |
|
||||
} else { |
|
||||
setState(() { |
|
||||
flash = false; |
|
||||
frontFlash = false; |
|
||||
}); |
|
||||
} |
|
||||
} catch (e) {} |
|
||||
} |
|
||||
|
|
||||
haltRecord() async { |
|
||||
XFile recorded = await stopVideoRecording(); |
|
||||
if (recorded != null) |
|
||||
Navigator.of(context).pushReplacement( |
|
||||
PageRouteBuilder( |
|
||||
opaque: false, |
|
||||
pageBuilder: (_, __, ___) => VideoReview( |
|
||||
video: recorded.path, |
|
||||
campaignID: widget.campaignID, |
|
||||
recorded: true, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Future<Uint8List> generateThumbnail(video) async { |
|
||||
try { |
|
||||
Uint8List thumbnail; |
|
||||
|
|
||||
thumbnail = await thumb.VideoThumbnail.thumbnailData( |
|
||||
video: video, |
|
||||
imageFormat: thumb.ImageFormat.JPEG, |
|
||||
maxWidth: 0, |
|
||||
maxHeight: 0, |
|
||||
timeMs: 1, |
|
||||
quality: 100, |
|
||||
); |
|
||||
return thumbnail; |
|
||||
} catch (e) { |
|
||||
print("Error :::: " + e); |
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
if (widget.connectedCameras == null || |
|
||||
widget.connectedCameras.length == 0) { |
|
||||
availableCameras().then((value) { |
|
||||
widget.connectedCameras = value; |
|
||||
onNewCameraSelected(widget.connectedCameras.first); |
|
||||
}); |
|
||||
} else { |
|
||||
onNewCameraSelected(widget.connectedCameras.first); |
|
||||
} |
|
||||
_recordingAnimationController = |
|
||||
new AnimationController(vsync: this, duration: Duration(seconds: 1)); |
|
||||
|
|
||||
_recordingAnimationController.repeat(reverse: true); |
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
sayCheese() async { |
|
||||
try { |
|
||||
if (flash && !frontFlash) |
|
||||
await _controller.setFlashMode(FlashMode.always); |
|
||||
await _controller.startVideoRecording(); |
|
||||
} catch (e) { |
|
||||
print(e); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Future<XFile> stopVideoRecording() async { |
|
||||
if (!_controller.value.isRecordingVideo) { |
|
||||
return null; |
|
||||
} |
|
||||
|
|
||||
try { |
|
||||
return _controller.stopVideoRecording(); |
|
||||
} on CameraException catch (e) { |
|
||||
print(e); |
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void onNewCameraSelected(CameraDescription cameraDescription) async { |
|
||||
if (_controller != null) { |
|
||||
await _controller.dispose(); |
|
||||
} |
|
||||
_controller = CameraController( |
|
||||
cameraDescription, |
|
||||
ResolutionPreset.high, |
|
||||
enableAudio: true, |
|
||||
imageFormatGroup: ImageFormatGroup.jpeg, |
|
||||
); |
|
||||
|
|
||||
// If the controller is updated then update the UI. |
|
||||
_controller.addListener(() { |
|
||||
if (mounted) setState(() {}); |
|
||||
if (_controller.value.hasError) { |
|
||||
print('Camera error ${_controller.value.errorDescription}'); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
try { |
|
||||
await _controller.initialize(); |
|
||||
_controller.lockCaptureOrientation(DeviceOrientation.portraitUp); |
|
||||
_controller.setFocusMode(FocusMode.auto); |
|
||||
} on CameraException catch (e) { |
|
||||
print(e); |
|
||||
} |
|
||||
|
|
||||
if (mounted) { |
|
||||
setState(() {}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void onHocusFocus(TapDownDetails details, BoxConstraints constraints) { |
|
||||
final offset = Offset( |
|
||||
details.localPosition.dx / constraints.maxWidth, |
|
||||
details.localPosition.dy / constraints.maxHeight, |
|
||||
); |
|
||||
_controller.setExposurePoint(offset); |
|
||||
_controller.setFocusPoint(offset); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() { |
|
||||
_controller?.dispose(); |
|
||||
_recordingAnimationController.dispose(); |
|
||||
super.dispose(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
if (_controller == null || !_controller.value.isInitialized) { |
|
||||
return Container( |
|
||||
color: Colors.black, |
|
||||
); |
|
||||
} else { |
|
||||
return Scaffold( |
|
||||
body: !gallery |
|
||||
? Stack( |
|
||||
children: [ |
|
||||
cameraWidget(context), |
|
||||
flashWidget(context), |
|
||||
cameraFlip(context), |
|
||||
cameraFlash(context), |
|
||||
recordingAnimation(context), |
|
||||
recordingCircle(context), |
|
||||
recorderWidget(context), |
|
||||
galleryPicker(context), |
|
||||
], |
|
||||
) |
|
||||
: Container(), |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
imgFromGallery() async { |
|
||||
try { |
|
||||
_controller?.dispose(); |
|
||||
setState(() { |
|
||||
gallery = true; |
|
||||
}); |
|
||||
final pickedFile = await picker.pickVideo( |
|
||||
source: ImageSource.gallery, |
|
||||
maxDuration: Duration(minutes: 1), |
|
||||
); |
|
||||
|
|
||||
if (pickedFile != null) { |
|
||||
return pickedFile.path; |
|
||||
} else { |
|
||||
onNewCameraSelected(widget.connectedCameras.first); |
|
||||
print('No image selected.'); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
print(e); |
|
||||
} |
|
||||
setState(() { |
|
||||
gallery = false; |
|
||||
}); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
Widget recordingCircle(context) { |
|
||||
return Align( |
|
||||
alignment: Alignment.bottomCenter, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
vertical: MediaQuery.of(context).size.width * 0.11, |
|
||||
), |
|
||||
height: 70, |
|
||||
width: 70, |
|
||||
child: CircularCountDownTimer( |
|
||||
duration: recordEnd, |
|
||||
initialDuration: 0, |
|
||||
controller: _controllerCountDown, |
|
||||
width: MediaQuery.of(context).size.width / 2, |
|
||||
height: MediaQuery.of(context).size.height / 2, |
|
||||
ringColor: Colors.grey[300], |
|
||||
fillColor: Colors.red, |
|
||||
backgroundColor: Colors.transparent, |
|
||||
autoStart: false, |
|
||||
strokeWidth: 5.5, |
|
||||
isTimerTextShown: false, |
|
||||
strokeCap: StrokeCap.round, |
|
||||
//onStart: startTimeout, |
|
||||
onComplete: haltRecord, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget recorderWidget(context) { |
|
||||
return Align( |
|
||||
alignment: Alignment.bottomCenter, |
|
||||
child: InkWell( |
|
||||
onTap: recording |
|
||||
? haltRecord |
|
||||
: () async { |
|
||||
await _controller.startVideoRecording(); |
|
||||
setState(() { |
|
||||
_controllerCountDown.start(); |
|
||||
recording = !recording; |
|
||||
}); |
|
||||
}, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
vertical: MediaQuery.of(context).size.width * 0.11, |
|
||||
), |
|
||||
height: 70, |
|
||||
width: 70, |
|
||||
child: Icon( |
|
||||
recording ? Icons.stop : Icons.video_camera_back, |
|
||||
color: Colors.white, |
|
||||
size: 25, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget galleryPicker(context) { |
|
||||
return Align( |
|
||||
alignment: Alignment.bottomLeft, |
|
||||
child: recording |
|
||||
? Container() |
|
||||
: InkWell( |
|
||||
onTap: () async { |
|
||||
String result = await imgFromGallery(); |
|
||||
if (result != null) { |
|
||||
// _controller.dispose(); |
|
||||
Navigator.pushReplacement( |
|
||||
context, |
|
||||
PageTransition( |
|
||||
type: PageTransitionType.leftToRight, |
|
||||
child: VideoReview( |
|
||||
video: result, |
|
||||
recorded: false, |
|
||||
campaignID: widget.campaignID, |
|
||||
), |
|
||||
)); |
|
||||
} |
|
||||
}, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: MediaQuery.of(context).size.width * 0.05, |
|
||||
vertical: MediaQuery.of(context).size.width * 0.11, |
|
||||
), |
|
||||
height: 70, |
|
||||
width: 70, |
|
||||
child: Icon( |
|
||||
Icons.photo, |
|
||||
color: Colors.white, |
|
||||
size: 27, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget recordingAnimation(context) { |
|
||||
if (!recording) |
|
||||
return Align( |
|
||||
alignment: Alignment.topLeft, |
|
||||
child: InkWell( |
|
||||
onTap: () { |
|
||||
Navigator.pop(context); |
|
||||
}, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: MediaQuery.of(context).size.width * 0.08, |
|
||||
vertical: MediaQuery.of(context).size.width * 0.11, |
|
||||
), |
|
||||
height: 35, |
|
||||
width: 35, |
|
||||
decoration: BoxDecoration( |
|
||||
//color: ColorFilterEngineLayer (0, 0, 0, 0.4), |
|
||||
shape: BoxShape.circle), |
|
||||
child: Icon( |
|
||||
Icons.arrow_back_ios, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
)); |
|
||||
else |
|
||||
return Align( |
|
||||
alignment: Alignment.topLeft, |
|
||||
child: Container( |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.center, |
|
||||
children: [ |
|
||||
Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: 5, |
|
||||
vertical: MediaQuery.of(context).size.width * 0.11, |
|
||||
), |
|
||||
padding: EdgeInsets.all(2.5), |
|
||||
height: 20, |
|
||||
width: 20, |
|
||||
decoration: BoxDecoration( |
|
||||
shape: BoxShape.circle, |
|
||||
border: Border.all( |
|
||||
color: Colors.red, |
|
||||
width: 2, |
|
||||
)), |
|
||||
child: FadeTransition( |
|
||||
opacity: _recordingAnimationController, |
|
||||
child: Container( |
|
||||
width: 20, |
|
||||
height: 20, |
|
||||
decoration: BoxDecoration( |
|
||||
shape: BoxShape.circle, |
|
||||
color: Colors.red, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget cameraFlash(context) { |
|
||||
return !recording |
|
||||
? Align( |
|
||||
alignment: Alignment.topRight, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: MediaQuery.of(context).size.width * 0.07, |
|
||||
vertical: MediaQuery.of(context).size.width * 0.25, |
|
||||
), |
|
||||
child: InkWell( |
|
||||
onTap: flashCamera, |
|
||||
child: Icon( |
|
||||
flash ? Icons.flash_on : Icons.flash_off, |
|
||||
color: flash ? tesoGold : Colors.white, |
|
||||
size: 30, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
) |
|
||||
: Container(); |
|
||||
} |
|
||||
|
|
||||
Widget cameraFlip(context) { |
|
||||
return !recording |
|
||||
? Align( |
|
||||
alignment: Alignment.topRight, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: MediaQuery.of(context).size.width * 0.06, |
|
||||
vertical: MediaQuery.of(context).size.width * 0.11, |
|
||||
), |
|
||||
child: InkWell( |
|
||||
onTap: flipCamera, |
|
||||
child: Icon( |
|
||||
Icons.cameraswitch_outlined, |
|
||||
color: Colors.white, |
|
||||
size: 40, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
) |
|
||||
: Container(); |
|
||||
} |
|
||||
|
|
||||
Widget flashWidget(context) { |
|
||||
return Visibility( |
|
||||
visible: frontFlash, |
|
||||
child: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
decoration: BoxDecoration( |
|
||||
color: Colors.white.withOpacity(0.4), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget cameraWidget(context) { |
|
||||
var camera = _controller.value; |
|
||||
final size = MediaQuery.of(context).size; |
|
||||
var scale = size.aspectRatio * camera.aspectRatio; |
|
||||
if (scale < 1) scale = 1 / scale; |
|
||||
|
|
||||
return Transform.scale( |
|
||||
scale: scale, |
|
||||
child: Center( |
|
||||
child: CameraPreview( |
|
||||
_controller, |
|
||||
child: LayoutBuilder( |
|
||||
builder: (BuildContext context, BoxConstraints constraints) { |
|
||||
return GestureDetector( |
|
||||
behavior: HitTestBehavior.opaque, |
|
||||
onTapDown: (details) => onHocusFocus(details, constraints), |
|
||||
); |
|
||||
}), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,45 +0,0 @@ |
|||||
/// The video file formats available for |
|
||||
/// generating the output trimmed video. |
|
||||
/// |
|
||||
/// The available formats are `mp4`, `mkv`, |
|
||||
/// `mov`, `flv`, `avi`, `wmv`& `gif`. |
|
||||
/// |
|
||||
/// If you define a custom `FFmpeg` command |
|
||||
/// then this will be ignored. |
|
||||
/// |
|
||||
class FileFormat { |
|
||||
const FileFormat._(this.index); |
|
||||
|
|
||||
final int index; |
|
||||
|
|
||||
static const FileFormat mp4 = FileFormat._(0); |
|
||||
static const FileFormat mkv = FileFormat._(1); |
|
||||
static const FileFormat mov = FileFormat._(2); |
|
||||
static const FileFormat flv = FileFormat._(3); |
|
||||
static const FileFormat avi = FileFormat._(4); |
|
||||
static const FileFormat wmv = FileFormat._(5); |
|
||||
static const FileFormat gif = FileFormat._(6); |
|
||||
|
|
||||
static const List<FileFormat> values = <FileFormat>[ |
|
||||
mp4, |
|
||||
mkv, |
|
||||
mov, |
|
||||
flv, |
|
||||
avi, |
|
||||
wmv, |
|
||||
gif, |
|
||||
]; |
|
||||
|
|
||||
@override |
|
||||
String toString() { |
|
||||
return const <int, String>{ |
|
||||
0: '.mp4', |
|
||||
1: '.mkv', |
|
||||
2: '.mov', |
|
||||
3: '.flv', |
|
||||
4: '.avi', |
|
||||
5: '.wmv', |
|
||||
6: '.gif', |
|
||||
}[index]; |
|
||||
} |
|
||||
} |
|
@ -1,32 +0,0 @@ |
|||||
/// Supported storage locations. |
|
||||
/// |
|
||||
/// * [temporaryDirectory] |
|
||||
/// |
|
||||
/// * [applicationDocumentsDirectory] |
|
||||
/// |
|
||||
/// * [externalStorageDirectory] |
|
||||
/// |
|
||||
class StorageDir { |
|
||||
const StorageDir._(this.index); |
|
||||
|
|
||||
final int index; |
|
||||
|
|
||||
static const StorageDir temporaryDirectory = StorageDir._(0); |
|
||||
static const StorageDir applicationDocumentsDirectory = StorageDir._(1); |
|
||||
static const StorageDir externalStorageDirectory = StorageDir._(2); |
|
||||
|
|
||||
static const List<StorageDir> values = <StorageDir>[ |
|
||||
temporaryDirectory, |
|
||||
applicationDocumentsDirectory, |
|
||||
externalStorageDirectory, |
|
||||
]; |
|
||||
|
|
||||
@override |
|
||||
String toString() { |
|
||||
return const <int, String>{ |
|
||||
0: 'temporaryDirectory', |
|
||||
1: 'applicationDocumentsDirectory', |
|
||||
2: 'externalStorageDirectory', |
|
||||
}[index]; |
|
||||
} |
|
||||
} |
|
@ -1,81 +0,0 @@ |
|||||
import 'dart:typed_data'; |
|
||||
|
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:video_thumbnail/video_thumbnail.dart'; |
|
||||
|
|
||||
class ThumbnailViewer extends StatelessWidget { |
|
||||
final videoFile; |
|
||||
final videoDuration; |
|
||||
final thumbnailHeight; |
|
||||
final fit; |
|
||||
final int numberOfThumbnails; |
|
||||
final int quality; |
|
||||
|
|
||||
/// For showing the thumbnails generated from the video, |
|
||||
/// like a frame by frame preview |
|
||||
ThumbnailViewer({ |
|
||||
@required this.videoFile, |
|
||||
@required this.videoDuration, |
|
||||
@required this.thumbnailHeight, |
|
||||
@required this.numberOfThumbnails, |
|
||||
@required this.fit, |
|
||||
this.quality = 75, |
|
||||
}) : assert(videoFile != null), |
|
||||
assert(videoDuration != null), |
|
||||
assert(thumbnailHeight != null), |
|
||||
assert(numberOfThumbnails != null), |
|
||||
assert(quality != null); |
|
||||
|
|
||||
Stream<List<Uint8List>> generateThumbnail() async* { |
|
||||
final String _videoPath = videoFile.path; |
|
||||
|
|
||||
double _eachPart = videoDuration / numberOfThumbnails; |
|
||||
|
|
||||
List<Uint8List> _byteList = []; |
|
||||
|
|
||||
for (int i = 1; i <= numberOfThumbnails; i++) { |
|
||||
Uint8List _bytes; |
|
||||
_bytes = await VideoThumbnail.thumbnailData( |
|
||||
video: _videoPath, |
|
||||
imageFormat: ImageFormat.JPEG, |
|
||||
timeMs: (_eachPart * i).toInt(), |
|
||||
quality: quality, |
|
||||
); |
|
||||
|
|
||||
_byteList.add(_bytes); |
|
||||
|
|
||||
yield _byteList; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return StreamBuilder( |
|
||||
stream: generateThumbnail(), |
|
||||
builder: (context, snapshot) { |
|
||||
if (snapshot.hasData) { |
|
||||
List<Uint8List> _imageBytes = snapshot.data; |
|
||||
return ListView.builder( |
|
||||
scrollDirection: Axis.horizontal, |
|
||||
itemCount: snapshot.data.length, |
|
||||
itemBuilder: (context, index) { |
|
||||
return Container( |
|
||||
height: thumbnailHeight, |
|
||||
width: thumbnailHeight, |
|
||||
child: Image( |
|
||||
image: MemoryImage(_imageBytes[index]), |
|
||||
fit: fit, |
|
||||
), |
|
||||
); |
|
||||
}); |
|
||||
} else { |
|
||||
return Container( |
|
||||
color: Colors.grey[900], |
|
||||
height: thumbnailHeight, |
|
||||
width: double.maxFinite, |
|
||||
); |
|
||||
} |
|
||||
}, |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,537 +0,0 @@ |
|||||
import 'dart:io'; |
|
||||
|
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/Campaign/Video/Trimmer/thumbnail_viewer.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/Campaign/Video/Trimmer/trim_editor_painter.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/Campaign/Video/Trimmer/trimmer.dart'; |
|
||||
import 'package:video_player/video_player.dart'; |
|
||||
|
|
||||
VideoPlayerController videoPlayerController; |
|
||||
|
|
||||
class TrimEditor extends StatefulWidget { |
|
||||
/// For defining the total trimmer area width |
|
||||
final double viewerWidth; |
|
||||
|
|
||||
/// For defining the total trimmer area height |
|
||||
final double viewerHeight; |
|
||||
|
|
||||
/// For defining the image fit type of each thumbnail image. |
|
||||
/// |
|
||||
/// By default it is set to `BoxFit.fitHeight`. |
|
||||
final BoxFit fit; |
|
||||
|
|
||||
/// For defining the maximum length of the output video. |
|
||||
final Duration maxVideoLength; |
|
||||
|
|
||||
/// For specifying a size to the holder at the |
|
||||
/// two ends of the video trimmer area, while it is `idle`. |
|
||||
/// |
|
||||
/// By default it is set to `5.0`. |
|
||||
final double circleSize; |
|
||||
|
|
||||
/// For specifying a size to the holder at |
|
||||
/// the two ends of the video trimmer area, while it is being |
|
||||
/// `dragged`. |
|
||||
/// |
|
||||
/// By default it is set to `8.0`. |
|
||||
final double circleSizeOnDrag; |
|
||||
|
|
||||
/// For specifying a color to the circle. |
|
||||
/// |
|
||||
/// By default it is set to `Colors.white`. |
|
||||
final Color circlePaintColor; |
|
||||
|
|
||||
/// For specifying a color to the border of |
|
||||
/// the trim area. |
|
||||
/// |
|
||||
/// By default it is set to `Colors.white`. |
|
||||
final Color borderPaintColor; |
|
||||
|
|
||||
/// For specifying a color to the video |
|
||||
/// scrubber inside the trim area. |
|
||||
/// |
|
||||
/// By default it is set to `Colors.white`. |
|
||||
final Color scrubberPaintColor; |
|
||||
|
|
||||
/// For specifying the quality of each |
|
||||
/// generated image thumbnail, to be displayed in the trimmer |
|
||||
/// area. |
|
||||
final int thumbnailQuality; |
|
||||
|
|
||||
/// For showing the start and the end point of the |
|
||||
/// video on top of the trimmer area. |
|
||||
/// |
|
||||
/// By default it is set to `true`. |
|
||||
final bool showDuration; |
|
||||
|
|
||||
/// For providing a `TextStyle` to the |
|
||||
/// duration text. |
|
||||
/// |
|
||||
/// By default it is set to `TextStyle(color: Colors.white)` |
|
||||
final TextStyle durationTextStyle; |
|
||||
|
|
||||
/// Callback to the video start position |
|
||||
/// |
|
||||
/// Returns the selected video start position in `milliseconds`. |
|
||||
final Function(double startValue) onChangeStart; |
|
||||
|
|
||||
/// Callback to the video end position. |
|
||||
/// |
|
||||
/// Returns the selected video end position in `milliseconds`. |
|
||||
final Function(double endValue) onChangeEnd; |
|
||||
|
|
||||
/// Callback to the video playback |
|
||||
/// state to know whether it is currently playing or paused. |
|
||||
/// |
|
||||
/// Returns a `boolean` value. If `true`, video is currently |
|
||||
/// playing, otherwise paused. |
|
||||
final Function(bool isPlaying) onChangePlaybackState; |
|
||||
|
|
||||
/// Widget for displaying the video trimmer. |
|
||||
/// |
|
||||
/// This has frame wise preview of the video with a |
|
||||
/// slider for selecting the part of the video to be |
|
||||
/// trimmed. |
|
||||
/// |
|
||||
/// The required parameters are [viewerWidth] & [viewerHeight] |
|
||||
/// |
|
||||
/// * [viewerWidth] to define the total trimmer area width. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [viewerHeight] to define the total trimmer area height. |
|
||||
/// |
|
||||
/// |
|
||||
/// The optional parameters are: |
|
||||
/// |
|
||||
/// * [fit] for specifying the image fit type of each thumbnail image. |
|
||||
/// By default it is set to `BoxFit.fitHeight`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [maxVideoLength] for specifying the maximum length of the |
|
||||
/// output video. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [circleSize] for specifying a size to the holder at the |
|
||||
/// two ends of the video trimmer area, while it is `idle`. |
|
||||
/// By default it is set to `5.0`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [circleSizeOnDrag] for specifying a size to the holder at |
|
||||
/// the two ends of the video trimmer area, while it is being |
|
||||
/// `dragged`. By default it is set to `8.0`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [circlePaintColor] for specifying a color to the circle. |
|
||||
/// By default it is set to `Colors.white`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [borderPaintColor] for specifying a color to the border of |
|
||||
/// the trim area. By default it is set to `Colors.white`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [scrubberPaintColor] for specifying a color to the video |
|
||||
/// scrubber inside the trim area. By default it is set to |
|
||||
/// `Colors.white`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [thumbnailQuality] for specifying the quality of each |
|
||||
/// generated image thumbnail, to be displayed in the trimmer |
|
||||
/// area. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [showDuration] for showing the start and the end point of the |
|
||||
/// video on top of the trimmer area. By default it is set to `true`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [durationTextStyle] is for providing a `TextStyle` to the |
|
||||
/// duration text. By default it is set to |
|
||||
/// `TextStyle(color: Colors.white)` |
|
||||
/// |
|
||||
/// |
|
||||
/// * [onChangeStart] is a callback to the video start position. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [onChangeEnd] is a callback to the video end position. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [onChangePlaybackState] is a callback to the video playback |
|
||||
/// state to know whether it is currently playing or paused. |
|
||||
/// |
|
||||
TrimEditor({ |
|
||||
@required this.viewerWidth, |
|
||||
@required this.viewerHeight, |
|
||||
this.fit = BoxFit.fitHeight, |
|
||||
this.maxVideoLength = const Duration(milliseconds: 0), |
|
||||
this.circleSize = 5.0, |
|
||||
this.circleSizeOnDrag = 8.0, |
|
||||
this.circlePaintColor = Colors.white, |
|
||||
this.borderPaintColor = Colors.white, |
|
||||
this.scrubberPaintColor = Colors.white, |
|
||||
this.thumbnailQuality = 75, |
|
||||
this.showDuration = true, |
|
||||
this.durationTextStyle = const TextStyle( |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
this.onChangeStart, |
|
||||
this.onChangeEnd, |
|
||||
this.onChangePlaybackState, |
|
||||
}) : assert(viewerWidth != null), |
|
||||
assert(viewerHeight != null), |
|
||||
assert(fit != null), |
|
||||
assert(maxVideoLength != null), |
|
||||
assert(circleSize != null), |
|
||||
assert(circleSizeOnDrag != null), |
|
||||
assert(circlePaintColor != null), |
|
||||
assert(borderPaintColor != null), |
|
||||
assert(scrubberPaintColor != null), |
|
||||
assert(thumbnailQuality != null), |
|
||||
assert(showDuration != null), |
|
||||
assert(durationTextStyle != null); |
|
||||
|
|
||||
@override |
|
||||
_TrimEditorState createState() => _TrimEditorState(); |
|
||||
} |
|
||||
|
|
||||
class _TrimEditorState extends State<TrimEditor> with TickerProviderStateMixin { |
|
||||
File _videoFile; |
|
||||
|
|
||||
double _videoStartPos = 0.0; |
|
||||
double _videoEndPos = 0.0; |
|
||||
|
|
||||
bool _canUpdateStart = true; |
|
||||
bool _isLeftDrag = true; |
|
||||
|
|
||||
Offset _startPos = Offset(0, 0); |
|
||||
Offset _endPos = Offset(0, 0); |
|
||||
|
|
||||
double _startFraction = 0.0; |
|
||||
double _endFraction = 1.0; |
|
||||
|
|
||||
int _videoDuration = 0; |
|
||||
int _currentPosition = 0; |
|
||||
|
|
||||
double _thumbnailViewerW = 0.0; |
|
||||
double _thumbnailViewerH = 0.0; |
|
||||
|
|
||||
int _numberOfThumbnails = 0; |
|
||||
|
|
||||
double _circleSize; |
|
||||
|
|
||||
double fraction; |
|
||||
double maxLengthPixels; |
|
||||
|
|
||||
ThumbnailViewer thumbnailWidget; |
|
||||
|
|
||||
Animation<double> _scrubberAnimation; |
|
||||
AnimationController _animationController; |
|
||||
Tween<double> _linearTween; |
|
||||
|
|
||||
Future<void> _initializeVideoController() async { |
|
||||
if (_videoFile != null) { |
|
||||
videoPlayerController.addListener(() { |
|
||||
final bool isPlaying = videoPlayerController.value.isPlaying; |
|
||||
|
|
||||
if (isPlaying) { |
|
||||
widget.onChangePlaybackState(true); |
|
||||
setState(() { |
|
||||
_currentPosition = |
|
||||
videoPlayerController.value.position.inMilliseconds; |
|
||||
|
|
||||
if (_currentPosition > _videoEndPos.toInt()) { |
|
||||
widget.onChangePlaybackState(false); |
|
||||
videoPlayerController.pause(); |
|
||||
_animationController.stop(); |
|
||||
} else { |
|
||||
if (!_animationController.isAnimating) { |
|
||||
widget.onChangePlaybackState(true); |
|
||||
_animationController.forward(); |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
} else { |
|
||||
if (videoPlayerController.value.isInitialized) { |
|
||||
if (_animationController != null) { |
|
||||
if ((_scrubberAnimation.value).toInt() == (_endPos.dx).toInt()) { |
|
||||
_animationController.reset(); |
|
||||
} |
|
||||
_animationController.stop(); |
|
||||
widget.onChangePlaybackState(false); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
videoPlayerController.setVolume(1.0); |
|
||||
_videoDuration = videoPlayerController.value.duration.inMilliseconds; |
|
||||
print(_videoFile.path); |
|
||||
|
|
||||
_videoEndPos = fraction != null |
|
||||
? _videoDuration.toDouble() * fraction |
|
||||
: _videoDuration.toDouble(); |
|
||||
|
|
||||
widget.onChangeEnd(_videoEndPos); |
|
||||
|
|
||||
final ThumbnailViewer _thumbnailWidget = ThumbnailViewer( |
|
||||
videoFile: _videoFile, |
|
||||
videoDuration: _videoDuration, |
|
||||
fit: widget.fit, |
|
||||
thumbnailHeight: _thumbnailViewerH, |
|
||||
numberOfThumbnails: _numberOfThumbnails, |
|
||||
quality: widget.thumbnailQuality, |
|
||||
); |
|
||||
thumbnailWidget = _thumbnailWidget; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void _setVideoStartPosition(DragUpdateDetails details) async { |
|
||||
if (!(_startPos.dx + details.delta.dx < 0) && |
|
||||
!(_startPos.dx + details.delta.dx > _thumbnailViewerW) && |
|
||||
!(_startPos.dx + details.delta.dx > _endPos.dx)) { |
|
||||
if (maxLengthPixels != null) { |
|
||||
if (!(_endPos.dx - _startPos.dx - details.delta.dx > maxLengthPixels)) { |
|
||||
setState(() { |
|
||||
if (!(_startPos.dx + details.delta.dx < 0)) |
|
||||
_startPos += details.delta; |
|
||||
|
|
||||
_startFraction = (_startPos.dx / _thumbnailViewerW); |
|
||||
|
|
||||
_videoStartPos = _videoDuration * _startFraction; |
|
||||
widget.onChangeStart(_videoStartPos); |
|
||||
}); |
|
||||
await videoPlayerController.pause(); |
|
||||
await videoPlayerController |
|
||||
.seekTo(Duration(milliseconds: _videoStartPos.toInt())); |
|
||||
_linearTween.begin = _startPos.dx; |
|
||||
_animationController.duration = |
|
||||
Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()); |
|
||||
_animationController.reset(); |
|
||||
} |
|
||||
} else { |
|
||||
setState(() { |
|
||||
if (!(_startPos.dx + details.delta.dx < 0)) |
|
||||
_startPos += details.delta; |
|
||||
|
|
||||
_startFraction = (_startPos.dx / _thumbnailViewerW); |
|
||||
|
|
||||
_videoStartPos = _videoDuration * _startFraction; |
|
||||
widget.onChangeStart(_videoStartPos); |
|
||||
}); |
|
||||
await videoPlayerController.pause(); |
|
||||
await videoPlayerController |
|
||||
.seekTo(Duration(milliseconds: _videoStartPos.toInt())); |
|
||||
_linearTween.begin = _startPos.dx; |
|
||||
_animationController.duration = |
|
||||
Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()); |
|
||||
_animationController.reset(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void _setVideoEndPosition(DragUpdateDetails details) async { |
|
||||
if (!(_endPos.dx + details.delta.dx > _thumbnailViewerW) && |
|
||||
!(_endPos.dx + details.delta.dx < 0) && |
|
||||
!(_endPos.dx + details.delta.dx < _startPos.dx)) { |
|
||||
if (maxLengthPixels != null) { |
|
||||
if (!(_endPos.dx - _startPos.dx + details.delta.dx > maxLengthPixels)) { |
|
||||
setState(() { |
|
||||
_endPos += details.delta; |
|
||||
_endFraction = _endPos.dx / _thumbnailViewerW; |
|
||||
|
|
||||
_videoEndPos = _videoDuration * _endFraction; |
|
||||
widget.onChangeEnd(_videoEndPos); |
|
||||
}); |
|
||||
await videoPlayerController.pause(); |
|
||||
await videoPlayerController |
|
||||
.seekTo(Duration(milliseconds: _videoEndPos.toInt())); |
|
||||
_linearTween.end = _endPos.dx; |
|
||||
_animationController.duration = |
|
||||
Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()); |
|
||||
_animationController.reset(); |
|
||||
} |
|
||||
} else { |
|
||||
setState(() { |
|
||||
_endPos += details.delta; |
|
||||
_endFraction = _endPos.dx / _thumbnailViewerW; |
|
||||
|
|
||||
_videoEndPos = _videoDuration * _endFraction; |
|
||||
widget.onChangeEnd(_videoEndPos); |
|
||||
}); |
|
||||
await videoPlayerController.pause(); |
|
||||
await videoPlayerController |
|
||||
.seekTo(Duration(milliseconds: _videoEndPos.toInt())); |
|
||||
_linearTween.end = _endPos.dx; |
|
||||
_animationController.duration = |
|
||||
Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()); |
|
||||
_animationController.reset(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
super.initState(); |
|
||||
_circleSize = widget.circleSize; |
|
||||
|
|
||||
_videoFile = Trimmer.currentVideoFile; |
|
||||
_thumbnailViewerH = widget.viewerHeight; |
|
||||
|
|
||||
_numberOfThumbnails = widget.viewerWidth ~/ _thumbnailViewerH; |
|
||||
|
|
||||
_thumbnailViewerW = _numberOfThumbnails * _thumbnailViewerH; |
|
||||
|
|
||||
Duration totalDuration = videoPlayerController.value.duration; |
|
||||
|
|
||||
if (widget.maxVideoLength > Duration(milliseconds: 0) && |
|
||||
widget.maxVideoLength < totalDuration) { |
|
||||
if (widget.maxVideoLength < totalDuration) { |
|
||||
fraction = |
|
||||
widget.maxVideoLength.inMilliseconds / totalDuration.inMilliseconds; |
|
||||
|
|
||||
maxLengthPixels = _thumbnailViewerW * fraction; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
_initializeVideoController(); |
|
||||
_endPos = Offset( |
|
||||
maxLengthPixels != null ? maxLengthPixels : _thumbnailViewerW, |
|
||||
_thumbnailViewerH, |
|
||||
); |
|
||||
|
|
||||
// Defining the tween points |
|
||||
_linearTween = Tween(begin: _startPos.dx, end: _endPos.dx); |
|
||||
|
|
||||
_animationController = AnimationController( |
|
||||
vsync: this, |
|
||||
duration: Duration(milliseconds: (_videoEndPos - _videoStartPos).toInt()), |
|
||||
); |
|
||||
|
|
||||
_scrubberAnimation = _linearTween.animate(_animationController) |
|
||||
..addListener(() { |
|
||||
setState(() {}); |
|
||||
}) |
|
||||
..addStatusListener((status) { |
|
||||
if (status == AnimationStatus.completed) { |
|
||||
_animationController.stop(); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() { |
|
||||
videoPlayerController.pause(); |
|
||||
widget.onChangePlaybackState(false); |
|
||||
if (_videoFile != null) { |
|
||||
videoPlayerController.setVolume(0.0); |
|
||||
videoPlayerController.pause(); |
|
||||
videoPlayerController.dispose(); |
|
||||
widget.onChangePlaybackState(false); |
|
||||
} |
|
||||
super.dispose(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return GestureDetector( |
|
||||
onHorizontalDragStart: (DragStartDetails details) { |
|
||||
print("START"); |
|
||||
print(details.localPosition); |
|
||||
print((_startPos.dx - details.localPosition.dx).abs()); |
|
||||
print((_endPos.dx - details.localPosition.dx).abs()); |
|
||||
|
|
||||
if (_endPos.dx >= _startPos.dx) { |
|
||||
if ((_startPos.dx - details.localPosition.dx).abs() > |
|
||||
(_endPos.dx - details.localPosition.dx).abs()) { |
|
||||
setState(() { |
|
||||
_canUpdateStart = false; |
|
||||
}); |
|
||||
} else { |
|
||||
setState(() { |
|
||||
_canUpdateStart = true; |
|
||||
}); |
|
||||
} |
|
||||
} else { |
|
||||
if (_startPos.dx > details.localPosition.dx) { |
|
||||
_isLeftDrag = true; |
|
||||
} else { |
|
||||
_isLeftDrag = false; |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
onHorizontalDragEnd: (DragEndDetails details) { |
|
||||
setState(() { |
|
||||
_circleSize = widget.circleSize; |
|
||||
}); |
|
||||
}, |
|
||||
onHorizontalDragUpdate: (DragUpdateDetails details) { |
|
||||
_circleSize = widget.circleSizeOnDrag; |
|
||||
|
|
||||
if (_endPos.dx >= _startPos.dx) { |
|
||||
_isLeftDrag = false; |
|
||||
if (_canUpdateStart && _startPos.dx + details.delta.dx > 0) { |
|
||||
_isLeftDrag = false; // To prevent from scrolling over |
|
||||
_setVideoStartPosition(details); |
|
||||
} else if (!_canUpdateStart && |
|
||||
_endPos.dx + details.delta.dx < _thumbnailViewerW) { |
|
||||
_isLeftDrag = true; // To prevent from scrolling over |
|
||||
_setVideoEndPosition(details); |
|
||||
} |
|
||||
} else { |
|
||||
if (_isLeftDrag && _startPos.dx + details.delta.dx > 0) { |
|
||||
_setVideoStartPosition(details); |
|
||||
} else if (!_isLeftDrag && |
|
||||
_endPos.dx + details.delta.dx < _thumbnailViewerW) { |
|
||||
_setVideoEndPosition(details); |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
child: Column( |
|
||||
mainAxisSize: MainAxisSize.min, |
|
||||
children: <Widget>[ |
|
||||
widget.showDuration |
|
||||
? Container( |
|
||||
width: _thumbnailViewerW, |
|
||||
child: Padding( |
|
||||
padding: const EdgeInsets.only(bottom: 8.0), |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
||||
mainAxisSize: MainAxisSize.max, |
|
||||
children: <Widget>[ |
|
||||
Text( |
|
||||
Duration(milliseconds: _videoStartPos.toInt()) |
|
||||
.toString() |
|
||||
.split('.')[0], |
|
||||
style: widget.durationTextStyle, |
|
||||
), |
|
||||
Text( |
|
||||
Duration(milliseconds: _videoEndPos.toInt()) |
|
||||
.toString() |
|
||||
.split('.')[0], |
|
||||
style: widget.durationTextStyle, |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
) |
|
||||
: Container(), |
|
||||
CustomPaint( |
|
||||
foregroundPainter: TrimEditorPainter( |
|
||||
startPos: _startPos, |
|
||||
endPos: _endPos, |
|
||||
scrubberAnimationDx: _scrubberAnimation.value, |
|
||||
circleSize: _circleSize, |
|
||||
circlePaintColor: widget.circlePaintColor, |
|
||||
borderPaintColor: widget.borderPaintColor, |
|
||||
scrubberPaintColor: widget.scrubberPaintColor, |
|
||||
), |
|
||||
child: Container( |
|
||||
color: Colors.grey[900], |
|
||||
height: _thumbnailViewerH, |
|
||||
width: _thumbnailViewerW, |
|
||||
child: thumbnailWidget == null ? Column() : thumbnailWidget, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,150 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
class TrimEditorPainter extends CustomPainter { |
|
||||
/// To define the start offset |
|
||||
final Offset startPos; |
|
||||
|
|
||||
/// To define the end offset |
|
||||
final Offset endPos; |
|
||||
|
|
||||
/// To define the horizontal length of the selected video area |
|
||||
final double scrubberAnimationDx; |
|
||||
|
|
||||
/// For specifying a size to the holder at the |
|
||||
/// two ends of the video trimmer area, while it is `idle`. |
|
||||
/// By default it is set to `0.5`. |
|
||||
final double circleSize; |
|
||||
|
|
||||
/// For specifying the width of the border around |
|
||||
/// the trim area. By default it is set to `3`. |
|
||||
final double borderWidth; |
|
||||
|
|
||||
/// For specifying the width of the video scrubber |
|
||||
final double scrubberWidth; |
|
||||
|
|
||||
/// For specifying whether to show the scrubber |
|
||||
final bool showScrubber; |
|
||||
|
|
||||
/// For specifying a color to the border of |
|
||||
/// the trim area. By default it is set to `Colors.white`. |
|
||||
final Color borderPaintColor; |
|
||||
|
|
||||
/// For specifying a color to the circle. |
|
||||
/// By default it is set to `Colors.white` |
|
||||
final Color circlePaintColor; |
|
||||
|
|
||||
/// For specifying a color to the video |
|
||||
/// scrubber inside the trim area. By default it is set to |
|
||||
/// `Colors.white`. |
|
||||
final Color scrubberPaintColor; |
|
||||
|
|
||||
/// For drawing the trim editor slider |
|
||||
/// |
|
||||
/// The required parameters are [startPos], [endPos] |
|
||||
/// & [scrubberAnimationDx] |
|
||||
/// |
|
||||
/// * [startPos] to define the start offset |
|
||||
/// |
|
||||
/// |
|
||||
/// * [endPos] to define the end offset |
|
||||
/// |
|
||||
/// |
|
||||
/// * [scrubberAnimationDx] to define the horizontal length of the |
|
||||
/// selected video area |
|
||||
/// |
|
||||
/// |
|
||||
/// The optional parameters are: |
|
||||
/// |
|
||||
/// * [circleSize] for specifying a size to the holder at the |
|
||||
/// two ends of the video trimmer area, while it is `idle`. |
|
||||
/// By default it is set to `0.5`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [borderWidth] for specifying the width of the border around |
|
||||
/// the trim area. By default it is set to `3`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [scrubberWidth] for specifying the width of the video scrubber |
|
||||
/// |
|
||||
/// |
|
||||
/// * [showScrubber] for specifying whether to show the scrubber |
|
||||
/// |
|
||||
/// |
|
||||
/// * [borderPaintColor] for specifying a color to the border of |
|
||||
/// the trim area. By default it is set to `Colors.white`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [circlePaintColor] for specifying a color to the circle. |
|
||||
/// By default it is set to `Colors.white`. |
|
||||
/// |
|
||||
/// |
|
||||
/// * [scrubberPaintColor] for specifying a color to the video |
|
||||
/// scrubber inside the trim area. By default it is set to |
|
||||
/// `Colors.white`. |
|
||||
/// |
|
||||
TrimEditorPainter({ |
|
||||
@required this.startPos, |
|
||||
@required this.endPos, |
|
||||
@required this.scrubberAnimationDx, |
|
||||
this.circleSize = 0.5, |
|
||||
this.borderWidth = 3, |
|
||||
this.scrubberWidth = 1, |
|
||||
this.showScrubber = true, |
|
||||
this.borderPaintColor = Colors.white, |
|
||||
this.circlePaintColor = Colors.white, |
|
||||
this.scrubberPaintColor = Colors.white, |
|
||||
}) : assert(startPos != null), |
|
||||
assert(endPos != null), |
|
||||
assert(scrubberAnimationDx != null), |
|
||||
assert(circleSize != null), |
|
||||
assert(borderWidth != null), |
|
||||
assert(scrubberWidth != null), |
|
||||
assert(showScrubber != null), |
|
||||
assert(borderPaintColor != null), |
|
||||
assert(circlePaintColor != null), |
|
||||
assert(scrubberPaintColor != null); |
|
||||
|
|
||||
@override |
|
||||
void paint(Canvas canvas, Size size) { |
|
||||
var borderPaint = Paint() |
|
||||
..color = borderPaintColor |
|
||||
..strokeWidth = borderWidth |
|
||||
..style = PaintingStyle.stroke |
|
||||
..strokeCap = StrokeCap.round; |
|
||||
|
|
||||
var circlePaint = Paint() |
|
||||
..color = circlePaintColor |
|
||||
..strokeWidth = 1 |
|
||||
..style = PaintingStyle.fill |
|
||||
..strokeCap = StrokeCap.round; |
|
||||
|
|
||||
var scrubberPaint = Paint() |
|
||||
..color = scrubberPaintColor |
|
||||
..strokeWidth = scrubberWidth |
|
||||
..style = PaintingStyle.stroke |
|
||||
..strokeCap = StrokeCap.round; |
|
||||
|
|
||||
final rect = Rect.fromPoints(startPos, endPos); |
|
||||
|
|
||||
if (showScrubber) { |
|
||||
if (scrubberAnimationDx.toInt() > startPos.dx.toInt()) { |
|
||||
canvas.drawLine( |
|
||||
Offset(scrubberAnimationDx, 0), |
|
||||
Offset(scrubberAnimationDx, 0) + Offset(0, endPos.dy), |
|
||||
scrubberPaint, |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
canvas.drawRect(rect, borderPaint); |
|
||||
canvas.drawCircle( |
|
||||
startPos + Offset(0, endPos.dy / 2), circleSize, circlePaint); |
|
||||
canvas.drawCircle( |
|
||||
endPos + Offset(0, -endPos.dy / 2), circleSize, circlePaint); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
bool shouldRepaint(CustomPainter oldDelegate) { |
|
||||
return true; |
|
||||
} |
|
||||
} |
|
@ -1,300 +0,0 @@ |
|||||
import 'dart:io'; |
|
||||
import 'package:path/path.dart'; |
|
||||
|
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter_ffmpeg/flutter_ffmpeg.dart'; |
|
||||
import 'package:intl/intl.dart'; |
|
||||
import 'package:path_provider/path_provider.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/Campaign/Video/Trimmer/file_formats.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/Campaign/Video/Trimmer/storage_dir.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/Campaign/Video/Trimmer/trim_editor.dart'; |
|
||||
import 'package:video_player/video_player.dart'; |
|
||||
|
|
||||
/// Helps in loading video from file, saving trimmed video to a file |
|
||||
/// and gives video playback controls. Some of the helpful methods |
|
||||
/// are: |
|
||||
/// * [loadVideo()] |
|
||||
/// * [saveTrimmedVideo()] |
|
||||
/// * [videPlaybackControl()] |
|
||||
class Trimmer { |
|
||||
static File currentVideoFile; |
|
||||
|
|
||||
final FlutterFFmpeg _flutterFFmpeg = new FlutterFFmpeg(); |
|
||||
|
|
||||
/// Loads a video using the path provided. |
|
||||
/// |
|
||||
/// Returns the loaded video file. |
|
||||
Future<void> loadVideo({@required File videoFile}) async { |
|
||||
currentVideoFile = videoFile; |
|
||||
if (currentVideoFile != null) { |
|
||||
videoPlayerController = VideoPlayerController.file(currentVideoFile); |
|
||||
await videoPlayerController.initialize().then((_) { |
|
||||
TrimEditor( |
|
||||
viewerHeight: 50, |
|
||||
viewerWidth: 50.0 * 8, |
|
||||
// currentVideoFile: currentVideoFile, |
|
||||
); |
|
||||
}); |
|
||||
// TrimEditor( |
|
||||
// viewerHeight: 50, |
|
||||
// viewerWidth: 50.0 * 8, |
|
||||
// // currentVideoFile: currentVideoFile, |
|
||||
// ); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Future<String> _createFolderInAppDocDir( |
|
||||
String folderName, |
|
||||
StorageDir storageDir, |
|
||||
) async { |
|
||||
Directory _directory; |
|
||||
|
|
||||
if (storageDir == null) { |
|
||||
_directory = await getApplicationDocumentsDirectory(); |
|
||||
} else { |
|
||||
switch (storageDir.toString()) { |
|
||||
case 'temporaryDirectory': |
|
||||
_directory = await getTemporaryDirectory(); |
|
||||
break; |
|
||||
|
|
||||
case 'applicationDocumentsDirectory': |
|
||||
_directory = await getApplicationDocumentsDirectory(); |
|
||||
break; |
|
||||
|
|
||||
case 'externalStorageDirectory': |
|
||||
_directory = await getExternalStorageDirectory(); |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Directory + folder name |
|
||||
final Directory _directoryFolder = |
|
||||
Directory('${_directory.path}/$folderName/'); |
|
||||
|
|
||||
if (await _directoryFolder.exists()) { |
|
||||
// If folder already exists return path |
|
||||
print('Exists'); |
|
||||
return _directoryFolder.path; |
|
||||
} else { |
|
||||
print('Creating'); |
|
||||
// If folder does not exists create folder and then return its path |
|
||||
final Directory _directoryNewFolder = |
|
||||
await _directoryFolder.create(recursive: true); |
|
||||
return _directoryNewFolder.path; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// Saves the trimmed video to file system. |
|
||||
/// |
|
||||
/// Returns the output video path |
|
||||
/// |
|
||||
/// The required parameters are [startValue] & [endValue]. |
|
||||
/// |
|
||||
/// The optional parameters are [videoFolderName], [videoFileName], |
|
||||
/// [outputFormat], [fpsGIF], [scaleGIF], [applyVideoEncoding]. |
|
||||
/// |
|
||||
/// The `@required` parameter [startValue] is for providing a starting point |
|
||||
/// to the trimmed video. To be specified in `milliseconds`. |
|
||||
/// |
|
||||
/// The `@required` parameter [endValue] is for providing an ending point |
|
||||
/// to the trimmed video. To be specified in `milliseconds`. |
|
||||
/// |
|
||||
/// The parameter [videoFolderName] is used to |
|
||||
/// pass a folder name which will be used for creating a new |
|
||||
/// folder in the selected directory. The default value for |
|
||||
/// it is `Trimmer`. |
|
||||
/// |
|
||||
/// The parameter [videoFileName] is used for giving |
|
||||
/// a new name to the trimmed video file. By default the |
|
||||
/// trimmed video is named as `<original_file_name>_trimmed.mp4`. |
|
||||
/// |
|
||||
/// The parameter [outputFormat] is used for providing a |
|
||||
/// file format to the trimmed video. This only accepts value |
|
||||
/// of [FileFormat] type. By default it is set to `FileFormat.mp4`, |
|
||||
/// which is for `mp4` files. |
|
||||
/// |
|
||||
/// The parameter [storageDir] can be used for providing a storage |
|
||||
/// location option. It accepts only [StorageDir] values. By default |
|
||||
/// it is set to [applicationDocumentsDirectory]. Some of the |
|
||||
/// storage types are: |
|
||||
/// |
|
||||
/// * [temporaryDirectory] (Only accessible from inside the app, can be |
|
||||
/// cleared at anytime) |
|
||||
/// |
|
||||
/// * [applicationDocumentsDirectory] (Only accessible from inside the app) |
|
||||
/// |
|
||||
/// * [externalStorageDirectory] (Supports only `Android`, accessible externally) |
|
||||
/// |
|
||||
/// The parameters [fpsGIF] & [scaleGIF] are used only if the |
|
||||
/// selected output format is `FileFormat.gif`. |
|
||||
/// |
|
||||
/// * [fpsGIF] for providing a FPS value (by default it is set |
|
||||
/// to `10`) |
|
||||
/// |
|
||||
/// |
|
||||
/// * [scaleGIF] for proving a width to output GIF, the height |
|
||||
/// is selected by maintaining the aspect ratio automatically (by |
|
||||
/// default it is set to `480`) |
|
||||
/// |
|
||||
/// |
|
||||
/// * [applyVideoEncoding] for specifying whether to apply video |
|
||||
/// encoding (by default it is set to `false`). |
|
||||
/// |
|
||||
/// |
|
||||
/// ADVANCED OPTION: |
|
||||
/// |
|
||||
/// If you want to give custom `FFmpeg` command, then define |
|
||||
/// [ffmpegCommand] & [customVideoFormat] strings. The `input path`, |
|
||||
/// `output path`, `start` and `end` position is already define. |
|
||||
/// |
|
||||
/// NOTE: The advanced option does not provide any safety check, so if wrong |
|
||||
/// video format is passed in [customVideoFormat], then the app may |
|
||||
/// crash. |
|
||||
/// |
|
||||
Future<String> saveTrimmedVideo({ |
|
||||
@required double startValue, |
|
||||
@required double endValue, |
|
||||
bool applyVideoEncoding = false, |
|
||||
FileFormat outputFormat, |
|
||||
String ffmpegCommand, |
|
||||
String customVideoFormat, |
|
||||
int fpsGIF, |
|
||||
int scaleGIF, |
|
||||
String videoFolderName, |
|
||||
String videoFileName, |
|
||||
StorageDir storageDir, |
|
||||
}) async { |
|
||||
final String _videoPath = currentVideoFile.path; |
|
||||
final String _videoName = basename(_videoPath).split('.')[0]; |
|
||||
|
|
||||
String _command; |
|
||||
|
|
||||
// Formatting Date and Time |
|
||||
String dateTime = DateFormat.yMMMd() |
|
||||
.addPattern('-') |
|
||||
.add_Hms() |
|
||||
.format(DateTime.now()) |
|
||||
.toString(); |
|
||||
|
|
||||
// String _resultString; |
|
||||
String _outputPath; |
|
||||
String _outputFormatString; |
|
||||
String formattedDateTime = dateTime.replaceAll(' ', ''); |
|
||||
|
|
||||
print("DateTime: $dateTime"); |
|
||||
print("Formatted: $formattedDateTime"); |
|
||||
|
|
||||
if (videoFolderName == null) { |
|
||||
videoFolderName = "Trimmer"; |
|
||||
} |
|
||||
|
|
||||
if (videoFileName == null) { |
|
||||
videoFileName = "${_videoName}_trimmed:$formattedDateTime"; |
|
||||
} |
|
||||
|
|
||||
videoFileName = videoFileName.replaceAll(' ', '_'); |
|
||||
|
|
||||
String path = await _createFolderInAppDocDir( |
|
||||
videoFolderName, |
|
||||
storageDir, |
|
||||
).whenComplete( |
|
||||
() => print("Retrieved Trimmer folder"), |
|
||||
); |
|
||||
|
|
||||
Duration startPoint = Duration(milliseconds: startValue.toInt()); |
|
||||
Duration endPoint = Duration(milliseconds: endValue.toInt()); |
|
||||
|
|
||||
// Checking the start and end point strings |
|
||||
print("Start: ${startPoint.toString()} & End: ${endPoint.toString()}"); |
|
||||
|
|
||||
print(path); |
|
||||
|
|
||||
if (outputFormat == null) { |
|
||||
if (Platform.isIOS) { |
|
||||
outputFormat = FileFormat.mp4; |
|
||||
} else { |
|
||||
outputFormat = FileFormat.mkv; |
|
||||
} |
|
||||
_outputFormatString = outputFormat.toString(); |
|
||||
print('OUTPUT: $_outputFormatString'); |
|
||||
} else { |
|
||||
_outputFormatString = outputFormat.toString(); |
|
||||
} |
|
||||
|
|
||||
String _trimLengthCommand = |
|
||||
' -ss $startPoint -i "$_videoPath" -t ${endPoint - startPoint} -avoid_negative_ts make_zero '; |
|
||||
|
|
||||
if (ffmpegCommand == null) { |
|
||||
_command = '$_trimLengthCommand -c:a copy '; |
|
||||
|
|
||||
if (!applyVideoEncoding) { |
|
||||
_command += '-c:v copy '; |
|
||||
} |
|
||||
|
|
||||
if (outputFormat == FileFormat.gif) { |
|
||||
if (fpsGIF == null) { |
|
||||
fpsGIF = 10; |
|
||||
} |
|
||||
if (scaleGIF == null) { |
|
||||
scaleGIF = 480; |
|
||||
} |
|
||||
_command = |
|
||||
'$_trimLengthCommand -vf "fps=$fpsGIF,scale=$scaleGIF:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 '; |
|
||||
} |
|
||||
} else { |
|
||||
_command = '$_trimLengthCommand $ffmpegCommand '; |
|
||||
_outputFormatString = customVideoFormat; |
|
||||
} |
|
||||
|
|
||||
_outputPath = '$path$videoFileName$_outputFormatString'; |
|
||||
|
|
||||
_command += '"$_outputPath"'; |
|
||||
|
|
||||
await _flutterFFmpeg.execute(_command).whenComplete(() { |
|
||||
print('Got value'); |
|
||||
debugPrint('Video successfuly saved'); |
|
||||
// _resultString = 'Video successfuly saved'; |
|
||||
}).catchError((error) { |
|
||||
print('Error'); |
|
||||
// _resultString = 'Couldn\'t save the video'; |
|
||||
debugPrint('Couldn\'t save the video'); |
|
||||
}); |
|
||||
|
|
||||
return _outputPath; |
|
||||
} |
|
||||
|
|
||||
/// For getting the video controller state, to know whether the |
|
||||
/// video is playing or paused currently. |
|
||||
/// |
|
||||
/// The two required parameters are [startValue] & [endValue] |
|
||||
/// |
|
||||
/// * [startValue] is the current starting point of the video. |
|
||||
/// * [endValue] is the current ending point of the video. |
|
||||
/// |
|
||||
/// Returns a `Future<bool>`, if `true` then video is playing |
|
||||
/// otherwise paused. |
|
||||
Future<bool> videPlaybackControl({ |
|
||||
@required double startValue, |
|
||||
@required double endValue, |
|
||||
}) async { |
|
||||
if (videoPlayerController.value.isPlaying) { |
|
||||
await videoPlayerController.pause(); |
|
||||
return false; |
|
||||
} else { |
|
||||
if (videoPlayerController.value.position.inMilliseconds >= |
|
||||
endValue.toInt()) { |
|
||||
await videoPlayerController |
|
||||
.seekTo(Duration(milliseconds: startValue.toInt())); |
|
||||
await videoPlayerController.play(); |
|
||||
return true; |
|
||||
} else { |
|
||||
await videoPlayerController.play(); |
|
||||
return true; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
File getVideoFile() { |
|
||||
return currentVideoFile; |
|
||||
} |
|
||||
} |
|
@ -1,92 +0,0 @@ |
|||||
import 'dart:typed_data'; |
|
||||
import 'package:flutter/cupertino.dart'; |
|
||||
import 'package:provider/provider.dart'; |
|
||||
import 'package:shared_preferences/shared_preferences.dart'; |
|
||||
import 'package:teso/Classes/Firebase/Posts.dart'; |
|
||||
import 'package:teso/Classes/Uploading.dart'; |
|
||||
import 'package:teso/Pages/PageWidgets/Posts/user_posted.dart'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; |
|
||||
import 'package:teso/Pages/PageWidgets/Uploads/Pending.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/@Generic/Camera/Video/RecordVideo.dart'; |
|
||||
import 'package:teso/providers/user_provider.dart'; |
|
||||
import 'package:teso/Pages/PageWidgets/Personal/Empty.dart'; |
|
||||
|
|
||||
class Posts extends StatefulWidget { |
|
||||
@override |
|
||||
_PostsState createState() => _PostsState(); |
|
||||
} |
|
||||
|
|
||||
class _PostsState extends State<Posts> { |
|
||||
// ScrollController _controller; |
|
||||
List<FBPosts> trends = <FBPosts>[]; |
|
||||
List<FBPosts> show = <FBPosts>[]; |
|
||||
int count; |
|
||||
Uint8List thumbnail; |
|
||||
SharedPreferences prefs; |
|
||||
bool loading = false; |
|
||||
|
|
||||
void postContent(context) async { |
|
||||
await Navigator.of(context).push( |
|
||||
PageRouteBuilder( |
|
||||
opaque: false, |
|
||||
pageBuilder: (_, __, ___) => RecordVideo(), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Consumer<UserProvider>( |
|
||||
builder: (context, UserProvider value, child) { |
|
||||
if (value.posts == null || value.posts.isEmpty) { |
|
||||
return buildEmpty(context, postContent); |
|
||||
} else { |
|
||||
return StaggeredGridView.count( |
|
||||
crossAxisCount: 3, |
|
||||
children: List.generate(value.posts.length, (int index) { |
|
||||
// if (index == 0 && provider.isNotEmpty) { |
|
||||
return buildPosted(context, value.posts.elementAt(index), 0.325); |
|
||||
// }else{ |
|
||||
// return buildPosted( |
|
||||
// context, value.posts.elementAt(index), 0.325); |
|
||||
// } |
|
||||
}), |
|
||||
staggeredTiles: List.generate( |
|
||||
value.posts.length, |
|
||||
(int index) { |
|
||||
return StaggeredTile.fit(1); |
|
||||
}, |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
}, |
|
||||
); |
|
||||
// : Center( |
|
||||
// child: CupertinoActivityIndicator( |
|
||||
// animating: true, |
|
||||
// radius: 15, |
|
||||
// ), |
|
||||
// ); |
|
||||
} |
|
||||
|
|
||||
Widget getTiles(BuildContext context, value) { |
|
||||
try { |
|
||||
List<Uploading> provider = value.getPending(); |
|
||||
if (value.pending != null) { |
|
||||
return SizedBox( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
child: ListView( |
|
||||
children: provider |
|
||||
.map<Widget>((item) => uploadTile(context, item)) |
|
||||
.toList()), |
|
||||
); |
|
||||
} else { |
|
||||
return Container(); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
return Container(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,196 +0,0 @@ |
|||||
import 'dart:convert'; |
|
||||
import 'dart:typed_data'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter/cupertino.dart'; |
|
||||
import 'package:provider/provider.dart'; |
|
||||
import 'package:shared_preferences/shared_preferences.dart'; |
|
||||
import 'package:teso/Classes/Uploading.dart'; |
|
||||
import 'package:teso/providers/user_provider.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
import 'package:http/http.dart' as http; |
|
||||
|
|
||||
class CreatePost extends StatefulWidget { |
|
||||
final String video; |
|
||||
final Uint8List thumbnail; |
|
||||
final String aspectRatio; |
|
||||
const CreatePost({ |
|
||||
Key key, |
|
||||
this.video, |
|
||||
this.thumbnail, |
|
||||
this.aspectRatio, |
|
||||
}) : super(key: key); |
|
||||
@override |
|
||||
_CreatePostState createState() => _CreatePostState(); |
|
||||
} |
|
||||
|
|
||||
class _CreatePostState extends State<CreatePost> { |
|
||||
String aspectRatio; |
|
||||
TextEditingController controller; |
|
||||
SharedPreferences prefs; |
|
||||
bool sending = false; |
|
||||
|
|
||||
void postVideo(context) async { |
|
||||
setState(() { |
|
||||
sending = true; |
|
||||
}); |
|
||||
try { |
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance(); |
|
||||
String token = prefs.getString("tokensTeso"); |
|
||||
Map<String, String> requestHeaders = {'Authorization': token}; |
|
||||
String urlLocation = tesoStreaming + "api/mobile/upload/authurl"; |
|
||||
var client = |
|
||||
await http.get(Uri.parse(urlLocation), headers: requestHeaders); |
|
||||
if (client.statusCode == 200) { |
|
||||
var details = jsonDecode(client.body); |
|
||||
String muxuploadsID = details["data"]["id"]; |
|
||||
String muxuploadsURL = details["data"]["url"]; |
|
||||
|
|
||||
Provider.of<UserProvider>(context, listen: false).uploadPost(Uploading( |
|
||||
id: DateTime.now().toString() + |
|
||||
widget.video.replaceAll("file://", ""), |
|
||||
aspect: widget.aspectRatio, |
|
||||
path: widget.video.replaceAll("file://", ""), |
|
||||
thumbnail: |
|
||||
widget.thumbnail != null ? base64Encode(widget.thumbnail) : null, |
|
||||
title: controller.text.isNotEmpty ? controller.text : "", |
|
||||
pending: 0, |
|
||||
muxuploadID: muxuploadsID, |
|
||||
muxuploadURL: muxuploadsURL, |
|
||||
)); |
|
||||
Navigator.pop(context); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
print(e); |
|
||||
} |
|
||||
setState(() { |
|
||||
sending = false; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
super.initState(); |
|
||||
controller = new TextEditingController(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() { |
|
||||
super.dispose(); |
|
||||
controller.dispose(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Scaffold( |
|
||||
appBar: PreferredSize( |
|
||||
child: AppBar( |
|
||||
automaticallyImplyLeading: true, |
|
||||
title: Text("Post"), |
|
||||
centerTitle: true, |
|
||||
), |
|
||||
preferredSize: Size.fromHeight(70.0), |
|
||||
), |
|
||||
body: SingleChildScrollView( |
|
||||
child: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
padding: EdgeInsets.all(MediaQuery.of(context).size.width * 0.025), |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: MediaQuery.of(context).size.width * 0.25, |
|
||||
height: MediaQuery.of(context).size.width * 0.35, |
|
||||
color: Colors.black, |
|
||||
child: widget.thumbnail != null |
|
||||
? Image.memory(widget.thumbnail) |
|
||||
: CupertinoActivityIndicator( |
|
||||
animating: true, |
|
||||
radius: 15, |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
width: (MediaQuery.of(context).size.width) - |
|
||||
(MediaQuery.of(context).size.width * 0.35), |
|
||||
height: MediaQuery.of(context).size.width * 0.35, |
|
||||
child: TextField( |
|
||||
decoration: InputDecoration( |
|
||||
border: OutlineInputBorder( |
|
||||
borderSide: BorderSide.none, |
|
||||
), |
|
||||
filled: true, |
|
||||
isDense: true, |
|
||||
labelText: "Say Something..", |
|
||||
labelStyle: TextStyle( |
|
||||
color: Colors.black54, |
|
||||
), |
|
||||
fillColor: Colors.white70, |
|
||||
), |
|
||||
controller: controller, |
|
||||
maxLines: null, |
|
||||
keyboardType: TextInputType.text, |
|
||||
textInputAction: TextInputAction.done, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
SizedBox( |
|
||||
height: 20, |
|
||||
), |
|
||||
Divider(), |
|
||||
Container( |
|
||||
margin: EdgeInsets.only( |
|
||||
top: 10, |
|
||||
), |
|
||||
child: Text( |
|
||||
"Teso businesses and other Teso users can see your post in their feeds and on your profile.", |
|
||||
textAlign: TextAlign.center, |
|
||||
style: TextStyle( |
|
||||
color: Colors.grey, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
floatingActionButton: !sending |
|
||||
? Container( |
|
||||
margin: EdgeInsets.all(20), |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
decoration: BoxDecoration( |
|
||||
borderRadius: BorderRadius.circular(15.0), |
|
||||
color: tesoBlue, |
|
||||
), |
|
||||
child: InkWell( |
|
||||
onTap: () => postVideo(context), |
|
||||
child: Center( |
|
||||
child: Text( |
|
||||
"NEXT", |
|
||||
style: TextStyle( |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
height: 50, |
|
||||
) |
|
||||
: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: 50, |
|
||||
child: Center( |
|
||||
child: CupertinoActivityIndicator( |
|
||||
animating: true, |
|
||||
radius: 15, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,585 +0,0 @@ |
|||||
import 'dart:typed_data'; |
|
||||
import 'package:cached_network_image/cached_network_image.dart'; |
|
||||
import 'package:cloud_firestore/cloud_firestore.dart'; |
|
||||
import 'package:flare_flutter/flare_actor.dart'; |
|
||||
import 'package:flare_flutter/flare_controls.dart'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter/services.dart'; |
|
||||
import 'package:flutter_bloc/flutter_bloc.dart'; |
|
||||
|
|
||||
import 'package:flutter/cupertino.dart'; |
|
||||
import 'package:page_transition/page_transition.dart'; |
|
||||
import 'package:provider/provider.dart'; |
|
||||
import 'package:shared_preferences/shared_preferences.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/CouponDetails.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/Post.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/PostFav.dart'; |
|
||||
import 'package:teso/Classes/TesoUser.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/Posts/comment.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/userProfile3P.dart'; |
|
||||
import 'package:teso/Services/video_controller_service.dart'; |
|
||||
import 'package:teso/blocs/video_player/video_player_bloc.dart'; |
|
||||
import 'package:teso/blocs/video_player/video_player_event.dart'; |
|
||||
import 'package:teso/blocs/video_player/video_player_state.dart'; |
|
||||
import 'package:teso/providers/user_provider.dart'; |
|
||||
import 'package:teso/util/SizeConfig.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
import 'package:numeral/numeral.dart'; |
|
||||
import 'package:teso/GeneralWidgets/widgets/video_player_widget.dart'; |
|
||||
import 'package:http/http.dart' as http; |
|
||||
import 'dart:convert'; |
|
||||
|
|
||||
// ignore: must_be_immutable |
|
||||
class ViewPost extends StatefulWidget { |
|
||||
Post postedAd; |
|
||||
TesoUser user; |
|
||||
bool friend; |
|
||||
final bool play; |
|
||||
|
|
||||
ViewPost({ |
|
||||
Key key, |
|
||||
this.postedAd, |
|
||||
this.user, |
|
||||
this.friend, |
|
||||
@required this.play, |
|
||||
// this.posts, |
|
||||
}) : super(key: key); |
|
||||
@override |
|
||||
_ViewPostState createState() => _ViewPostState(); |
|
||||
} |
|
||||
|
|
||||
class _ViewPostState extends State<ViewPost> { |
|
||||
bool favoured = false; |
|
||||
List<CouponDetails> coupons = <CouponDetails>[]; |
|
||||
Uint8List imageBitmap; |
|
||||
final FlareControls flareControls = FlareControls(); |
|
||||
bool campaignAd = false; |
|
||||
int likes = 0; |
|
||||
int comments = 0; |
|
||||
var userDoc; |
|
||||
var document; |
|
||||
|
|
||||
void sharing(Post ad) async { |
|
||||
await rootBundle |
|
||||
.load("assets/images/rawLogoOverlay.png") |
|
||||
.then((value) => setState(() { |
|
||||
imageBitmap = value.buffer.asUint8List(); |
|
||||
})); |
|
||||
Provider.of<UserProvider>(context, listen: false).downloadVideo( |
|
||||
ad.postID, ad.playbackID, ad.rendition, imageBitmap, context); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() { |
|
||||
rootBundle.evict("assets/images/rawLogoOverlay.png"); |
|
||||
super.dispose(); |
|
||||
} |
|
||||
|
|
||||
void likePost(Post ad) { |
|
||||
SharedPreferences.getInstance().then((value) { |
|
||||
String cid = value.getString("id"); |
|
||||
PostFav liked = new PostFav(); |
|
||||
liked.admirerId = cid; |
|
||||
liked.countId = DateTime.now().toString() + "$cid"; |
|
||||
liked.timestamp = DateTime.now().toIso8601String(); |
|
||||
liked.postId = ad.postID; |
|
||||
|
|
||||
setState(() { |
|
||||
// ad.likes.add(liked); |
|
||||
likes++; |
|
||||
favoured = true; |
|
||||
}); |
|
||||
|
|
||||
flareControls.play("like"); |
|
||||
Provider.of<UserProvider>(context, listen: false).addLike(liked); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
void dislikePost(Post ad) { |
|
||||
setState(() { |
|
||||
favoured = false; |
|
||||
likes--; |
|
||||
}); |
|
||||
Provider.of<UserProvider>(context, listen: false).deleteLike(ad.postID); |
|
||||
} |
|
||||
|
|
||||
void commentsDialog(BuildContext context) { |
|
||||
if (userDoc == null) { |
|
||||
FirebaseFirestore.instance |
|
||||
.collection("users") |
|
||||
.doc(widget.postedAd.publisherID) |
|
||||
.get() |
|
||||
.then((value) { |
|
||||
setState(() { |
|
||||
userDoc = value.data(); |
|
||||
}); |
|
||||
}); |
|
||||
} else { |
|
||||
showModalBottomSheet( |
|
||||
context: context, |
|
||||
shape: RoundedRectangleBorder( |
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20.0)), |
|
||||
), |
|
||||
builder: (BuildContext bc) { |
|
||||
return ClipRRect( |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topLeft: Radius.circular(20.0), |
|
||||
topRight: Radius.circular(20.0), |
|
||||
), |
|
||||
child: CommentSection( |
|
||||
postedAd: widget.postedAd, |
|
||||
user: TesoUser( |
|
||||
username: userDoc["username"], |
|
||||
userGUID: userDoc["id"], |
|
||||
)), |
|
||||
); |
|
||||
}, |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Future<void> getCampaignCoupons(String campaign) async { |
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance(); |
|
||||
Map<String, String> requestHeaders = { |
|
||||
'Content-type': 'application/json', |
|
||||
'Authorization': prefs.getString('tokensTeso') |
|
||||
}; |
|
||||
try { |
|
||||
var register2 = serverLocation + 'coupons/campaign_coupons'; |
|
||||
var client1 = await http.post( |
|
||||
Uri.parse(register2), |
|
||||
headers: requestHeaders, |
|
||||
body: json.encode(campaign), |
|
||||
); |
|
||||
if (client1.statusCode == 200) { |
|
||||
var details = jsonDecode(client1.body); |
|
||||
setState(() { |
|
||||
coupons = List<CouponDetails>.from( |
|
||||
details.map((model) => CouponDetails.fromJSON(model)).toList()); |
|
||||
// coupons.removeWhere( |
|
||||
// (element) => element.expiration.isAfter(DateTime.now())); |
|
||||
}); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
print(e); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
campaignAd = false; |
|
||||
_getDocuments(); |
|
||||
_likedListen(); |
|
||||
_commentsListen(); |
|
||||
FirebaseFirestore.instance |
|
||||
.collection("users") |
|
||||
.doc(widget.postedAd.publisherID) |
|
||||
.get() |
|
||||
.then((value) { |
|
||||
if (mounted) |
|
||||
setState(() { |
|
||||
userDoc = value.data(); |
|
||||
}); |
|
||||
}); |
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
_getDocuments() { |
|
||||
FirebaseFirestore.instance |
|
||||
.collection("posts") |
|
||||
.doc(widget.postedAd.postID) |
|
||||
.get() |
|
||||
.then((value) { |
|
||||
if (mounted) |
|
||||
setState(() { |
|
||||
document = value.data(); |
|
||||
if (document != null) { |
|
||||
if (document["campaignId"] != null) { |
|
||||
campaignAd = true; |
|
||||
getCampaignCoupons(document["campaignId"]); |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
_likedListen() { |
|
||||
SharedPreferences.getInstance().then((value) { |
|
||||
String cid = value.getString("id"); |
|
||||
FirebaseFirestore.instance |
|
||||
.collection("posts") |
|
||||
.doc(widget.postedAd.postID) |
|
||||
.collection("likes") |
|
||||
.snapshots() |
|
||||
.listen((event) { |
|
||||
if (mounted) { |
|
||||
setState(() { |
|
||||
favoured = |
|
||||
event.docs.any((element) => element.data()["admirerID"] == cid); |
|
||||
likes = event.docs.length; |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
_commentsListen() { |
|
||||
FirebaseFirestore.instance |
|
||||
.collection("posts") |
|
||||
.doc(widget.postedAd.postID) |
|
||||
.collection("comments") |
|
||||
.snapshots() |
|
||||
.listen((event) { |
|
||||
if (mounted) { |
|
||||
setState(() { |
|
||||
comments = event.docs.length; |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
SizeConfig().init(context); |
|
||||
return Scaffold( |
|
||||
body: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
color: Colors.black, |
|
||||
child: Stack( |
|
||||
children: [ |
|
||||
_buildVideoPlayer(widget.postedAd), |
|
||||
Align( |
|
||||
alignment: Alignment.bottomRight, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.only( |
|
||||
right: 10, |
|
||||
bottom: 30, |
|
||||
), |
|
||||
width: 50, |
|
||||
height: MediaQuery.of(context).size.width * 0.73, |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: 40, |
|
||||
height: 40, |
|
||||
decoration: BoxDecoration( |
|
||||
shape: BoxShape.circle, |
|
||||
border: Border.all( |
|
||||
color: Colors.black, |
|
||||
width: 1, |
|
||||
), |
|
||||
), |
|
||||
child: ClipRRect( |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topLeft: Radius.circular(90.0), |
|
||||
topRight: Radius.circular(90.0), |
|
||||
bottomLeft: Radius.circular(90), |
|
||||
bottomRight: Radius.circular(90), |
|
||||
), |
|
||||
child: InkWell( |
|
||||
onTap: widget.postedAd.publisherID != null |
|
||||
? () => Navigator.pushReplacement( |
|
||||
context, |
|
||||
PageTransition( |
|
||||
child: UserProfileThirdPerson( |
|
||||
user: new TesoUser( |
|
||||
username: userDoc["username"], |
|
||||
userGUID: userDoc["id"], |
|
||||
firstname: userDoc["firstname"], |
|
||||
lastname: userDoc["surname"], |
|
||||
), |
|
||||
), |
|
||||
type: PageTransitionType.fade, |
|
||||
), |
|
||||
) |
|
||||
: null, |
|
||||
child: CachedNetworkImage( |
|
||||
imageUrl: serverLocation + |
|
||||
"api/pulldp/" + |
|
||||
widget.postedAd.publisherID, |
|
||||
imageBuilder: (context, imageProvider) => |
|
||||
FadeInImage( |
|
||||
height: 90, |
|
||||
width: 90, |
|
||||
fit: BoxFit.fill, |
|
||||
image: imageProvider, |
|
||||
placeholder: |
|
||||
AssetImage("assets/images/tesoDP/dp1.png"), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
SizedBox( |
|
||||
height: 20, |
|
||||
), |
|
||||
Container( |
|
||||
height: 50, |
|
||||
child: InkWell( |
|
||||
onTap: () { |
|
||||
if (campaignAd) { |
|
||||
if (favoured) { |
|
||||
return null; |
|
||||
} else { |
|
||||
likePost(widget.postedAd); |
|
||||
} |
|
||||
} else { |
|
||||
if (favoured) { |
|
||||
dislikePost(widget.postedAd); |
|
||||
} else { |
|
||||
likePost(widget.postedAd); |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
child: new Wrap( |
|
||||
direction: Axis.vertical, |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: 50, |
|
||||
height: 30, |
|
||||
child: Center( |
|
||||
child: Icon( |
|
||||
Icons.favorite, |
|
||||
size: 30, |
|
||||
color: favoured ? Colors.red : Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
height: 20, |
|
||||
width: 50, |
|
||||
child: Center( |
|
||||
child: Text( |
|
||||
Numeral(likes).value().toString(), |
|
||||
style: TextStyle( |
|
||||
fontWeight: FontWeight.bold, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
SizedBox( |
|
||||
height: 20, |
|
||||
), |
|
||||
Container( |
|
||||
height: 50, |
|
||||
child: InkWell( |
|
||||
onTap: () => commentsDialog(context), |
|
||||
child: new Wrap( |
|
||||
direction: Axis.vertical, |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: 50, |
|
||||
height: 30, |
|
||||
child: Center( |
|
||||
child: Icon( |
|
||||
Icons.comment, |
|
||||
size: 30, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
height: 20, |
|
||||
width: 50, |
|
||||
child: Center( |
|
||||
child: Text( |
|
||||
Numeral(comments).value().toString(), |
|
||||
style: TextStyle( |
|
||||
fontWeight: FontWeight.bold, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
SizedBox( |
|
||||
height: 20, |
|
||||
), |
|
||||
Container( |
|
||||
height: 30, |
|
||||
child: InkWell( |
|
||||
onTap: () => sharing(widget.postedAd), |
|
||||
child: Icon( |
|
||||
Icons.share, |
|
||||
size: 30, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Align( |
|
||||
alignment: Alignment.bottomLeft, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: 10, |
|
||||
vertical: MediaQuery.of(context).size.height * 0.05, |
|
||||
), |
|
||||
child: new Wrap( |
|
||||
direction: Axis.vertical, |
|
||||
children: [ |
|
||||
new RichText( |
|
||||
maxLines: 5, |
|
||||
text: TextSpan( |
|
||||
text: userDoc != null ? "@" + userDoc["username"] : "", |
|
||||
style: TextStyle( |
|
||||
color: Colors.white, |
|
||||
fontSize: SizeConfig.safeBlockHorizontal * 4.3, |
|
||||
fontWeight: FontWeight.bold, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
SizedBox(height: 5), |
|
||||
Container( |
|
||||
margin: EdgeInsets.only(bottom: 20), |
|
||||
width: MediaQuery.of(context).size.width * 0.7, |
|
||||
child: Text( |
|
||||
document != null |
|
||||
? document["title"] != null |
|
||||
? document["title"] |
|
||||
: "" |
|
||||
: "", |
|
||||
style: TextStyle( |
|
||||
color: Colors.white, |
|
||||
fontSize: SizeConfig.safeBlockHorizontal * 4.3, |
|
||||
height: 1.5, |
|
||||
), |
|
||||
maxLines: 4, |
|
||||
overflow: TextOverflow.ellipsis, |
|
||||
textDirection: TextDirection.rtl, |
|
||||
textAlign: TextAlign.left, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Align( |
|
||||
alignment: Alignment.topLeft, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.only(top: 25), |
|
||||
child: IconButton( |
|
||||
onPressed: () => Navigator.of(context).pop(), |
|
||||
icon: new Icon( |
|
||||
Icons.arrow_back, |
|
||||
color: Colors.white, |
|
||||
size: 25.0, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget _buildVideoPlayer(Post ad) { |
|
||||
return BlocProvider<VideoPlayerBloc>( |
|
||||
create: (context) => VideoPlayerBloc( |
|
||||
RepositoryProvider.of<VideoControllerService>(context)) |
|
||||
..add(VideoSelectedEvent(ad)), |
|
||||
child: BlocBuilder<VideoPlayerBloc, VideoPlayerState>( |
|
||||
builder: (context, state) { |
|
||||
return Container(child: _getPlayer(context, state, ad)); |
|
||||
}, |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget _getPlayer(BuildContext context, VideoPlayerState state, Post ad) { |
|
||||
// final screenWidth = MediaQuery.of(context).size.width; |
|
||||
// final containerHeight = screenWidth / ASPECT_RATIO; |
|
||||
final containerHeight = MediaQuery.of(context).size.height; |
|
||||
if (state is VideoPlayerStateLoaded) { |
|
||||
return GestureDetector( |
|
||||
onDoubleTap: () { |
|
||||
if (campaignAd) { |
|
||||
if (favoured) { |
|
||||
return null; |
|
||||
} else { |
|
||||
likePost(ad); |
|
||||
} |
|
||||
} else { |
|
||||
if (favoured) { |
|
||||
dislikePost(ad); |
|
||||
} else { |
|
||||
likePost(ad); |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
child: Stack( |
|
||||
children: [ |
|
||||
VideoPlayerWidget( |
|
||||
key: Key(state.video.playbackID), |
|
||||
controller: state.controller, |
|
||||
ad: ad, |
|
||||
play: widget.play, |
|
||||
details: coupons, |
|
||||
), |
|
||||
Container( |
|
||||
width: double.infinity, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
child: Center( |
|
||||
child: SizedBox( |
|
||||
width: 80, |
|
||||
height: 80, |
|
||||
child: FlareActor( |
|
||||
'assets/like.flr', |
|
||||
controller: flareControls, |
|
||||
animation: 'idle', |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
if (state is VideoPlayerStateLoading) { |
|
||||
return Container( |
|
||||
height: containerHeight, |
|
||||
color: Colors.black, |
|
||||
child: Center( |
|
||||
child: CupertinoActivityIndicator( |
|
||||
animating: true, |
|
||||
radius: 15, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
if (state is VideoPlayerStateError) { |
|
||||
return Container( |
|
||||
height: containerHeight, |
|
||||
color: Colors.black, |
|
||||
child: Center( |
|
||||
child: Text(state.message), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
return Container( |
|
||||
height: containerHeight, |
|
||||
color: Colors.black, |
|
||||
child: Center( |
|
||||
child: CupertinoActivityIndicator( |
|
||||
animating: true, |
|
||||
radius: 15, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,515 +0,0 @@ |
|||||
import 'dart:typed_data'; |
|
||||
import 'package:cloud_firestore/cloud_firestore.dart'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter/services.dart'; |
|
||||
import 'package:flutter_bloc/flutter_bloc.dart'; |
|
||||
import 'package:provider/provider.dart'; |
|
||||
import 'package:shared_preferences/shared_preferences.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/Post.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/PostFav.dart'; |
|
||||
import 'package:teso/Classes/Firebase/Posts.dart'; |
|
||||
import 'package:teso/Classes/TesoUser.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/Posts/deletePost.dart'; |
|
||||
import 'package:teso/Services/uservideo_controller_service.dart'; |
|
||||
import 'package:teso/blocs/video_player/uservideo_player_bloc.dart'; |
|
||||
import 'package:teso/blocs/video_player/uservideo_player_event.dart'; |
|
||||
import 'package:teso/blocs/video_player/uservideo_player_state.dart'; |
|
||||
import 'package:teso/providers/user_provider.dart'; |
|
||||
import 'package:teso/util/SizeConfig.dart'; |
|
||||
import 'package:numeral/numeral.dart'; |
|
||||
import 'package:teso/GeneralWidgets/widgets/uservideo_player_widget.dart'; |
|
||||
|
|
||||
import 'comment.dart'; |
|
||||
|
|
||||
// ignore: must_be_immutable |
|
||||
class UserPosts extends StatefulWidget { |
|
||||
FBPosts postedAd; |
|
||||
|
|
||||
UserPosts({Key key, this.postedAd}) : super(key: key); |
|
||||
@override |
|
||||
_UserPostsState createState() => _UserPostsState(); |
|
||||
} |
|
||||
|
|
||||
class _UserPostsState extends State<UserPosts> { |
|
||||
bool favoured = false; |
|
||||
Uint8List imageBitmap; |
|
||||
var document; |
|
||||
var userDoc; |
|
||||
bool likeShow = false; |
|
||||
|
|
||||
void sharing() async { |
|
||||
await rootBundle |
|
||||
.load("assets/images/rawLogoOverlay.png") |
|
||||
.then((value) => setState(() { |
|
||||
imageBitmap = value.buffer.asUint8List(); |
|
||||
})); |
|
||||
Provider.of<UserProvider>(context, listen: false).downloadVideo( |
|
||||
widget.postedAd.postID, |
|
||||
widget.postedAd.playbackID, |
|
||||
widget.postedAd.rendition, |
|
||||
imageBitmap, |
|
||||
context); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() { |
|
||||
rootBundle.evict("assets/images/rawLogoOverlay.png"); |
|
||||
super.dispose(); |
|
||||
} |
|
||||
|
|
||||
void commentsDialog(BuildContext context) { |
|
||||
if (userDoc == null) { |
|
||||
FirebaseFirestore.instance |
|
||||
.collection("users") |
|
||||
.doc(widget.postedAd.publisherID) |
|
||||
.get() |
|
||||
.then((value) { |
|
||||
setState(() { |
|
||||
userDoc = value.data(); |
|
||||
}); |
|
||||
}); |
|
||||
} else { |
|
||||
showModalBottomSheet( |
|
||||
context: context, |
|
||||
shape: RoundedRectangleBorder( |
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20.0)), |
|
||||
), |
|
||||
builder: (BuildContext bc) { |
|
||||
return ClipRRect( |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topLeft: Radius.circular(20.0), |
|
||||
topRight: Radius.circular(20.0), |
|
||||
), |
|
||||
child: CommentSection( |
|
||||
postedAd: new Post( |
|
||||
aspect: widget.postedAd.aspect, |
|
||||
assetID: widget.postedAd.assetID, |
|
||||
playbackID: widget.postedAd.playbackID, |
|
||||
postID: widget.postedAd.postID, |
|
||||
publisherID: widget.postedAd.publisherID, |
|
||||
title: widget.postedAd.title, |
|
||||
rendition: widget.postedAd.rendition, |
|
||||
timestamp: widget.postedAd.timestamp, |
|
||||
), |
|
||||
user: TesoUser( |
|
||||
username: userDoc["username"], |
|
||||
userGUID: userDoc["id"], |
|
||||
)), |
|
||||
); |
|
||||
}, |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void likePost() { |
|
||||
setState(() { |
|
||||
likeShow = true; |
|
||||
}); |
|
||||
SharedPreferences.getInstance().then((value) { |
|
||||
String cid = value.getString("id"); |
|
||||
PostFav liked = new PostFav(); |
|
||||
liked.admirerId = cid; |
|
||||
liked.countId = DateTime.now().toString() + "$cid"; |
|
||||
liked.timestamp = DateTime.now().toIso8601String(); |
|
||||
liked.postId = widget.postedAd.postID; |
|
||||
|
|
||||
setState(() { |
|
||||
widget.postedAd.likes++; |
|
||||
favoured = true; |
|
||||
}); |
|
||||
Provider.of<UserProvider>(context, listen: false).addLike(liked); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
void dislikePost() { |
|
||||
SharedPreferences.getInstance().then((value) { |
|
||||
// String cid = value.getString("id"); |
|
||||
setState(() { |
|
||||
widget.postedAd.likes--; |
|
||||
favoured = false; |
|
||||
}); |
|
||||
Provider.of<UserProvider>(context, listen: false) |
|
||||
.deleteLike(widget.postedAd.postID); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
super.initState(); |
|
||||
_likedListen(); |
|
||||
FirebaseFirestore.instance |
|
||||
.collection("posts") |
|
||||
.doc(widget.postedAd.postID) |
|
||||
.get() |
|
||||
.then((value) { |
|
||||
setState(() { |
|
||||
document = value.data(); |
|
||||
}); |
|
||||
}); |
|
||||
FirebaseFirestore.instance |
|
||||
.collection("users") |
|
||||
.doc(widget.postedAd.publisherID) |
|
||||
.get() |
|
||||
.then((value) { |
|
||||
setState(() { |
|
||||
userDoc = value.data(); |
|
||||
}); |
|
||||
}); |
|
||||
// SharedPreferences.getInstance().then((value) { |
|
||||
// String cid = value.getString("id"); |
|
||||
// if (widget.postedAd.likes |
|
||||
// .map((e) => e.admirerId) |
|
||||
// .toList() |
|
||||
// .contains(cid)) { |
|
||||
// setState(() { |
|
||||
// favoured = true; |
|
||||
// }); |
|
||||
// } |
|
||||
// }); |
|
||||
// _future = initializeController(); |
|
||||
} |
|
||||
|
|
||||
_likedListen() { |
|
||||
SharedPreferences.getInstance().then((value) { |
|
||||
String cid = value.getString("id"); |
|
||||
FirebaseFirestore.instance |
|
||||
.collection("posts") |
|
||||
.doc(widget.postedAd.postID) |
|
||||
.collection("likes") |
|
||||
.snapshots() |
|
||||
.listen((event) { |
|
||||
setState(() { |
|
||||
favoured = |
|
||||
event.docs.any((element) => element.data()["admirerID"] == cid); |
|
||||
widget.postedAd.likes = event.docs.length; |
|
||||
}); |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Scaffold( |
|
||||
appBar: PreferredSize( |
|
||||
child: AppBar( |
|
||||
automaticallyImplyLeading: false, |
|
||||
backgroundColor: Colors.transparent, |
|
||||
leading: _backWidget(context), |
|
||||
), |
|
||||
preferredSize: Size.fromHeight(40)), |
|
||||
extendBodyBehindAppBar: true, |
|
||||
body: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
color: Colors.black, |
|
||||
child: Stack( |
|
||||
children: [ |
|
||||
_buildVideoPlayer(), |
|
||||
Align( |
|
||||
alignment: Alignment.bottomRight, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.only( |
|
||||
right: 10, |
|
||||
), |
|
||||
width: 50, |
|
||||
height: MediaQuery.of(context).size.width * 0.7, |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
_favoriteWidget(context), |
|
||||
SizedBox( |
|
||||
height: 20, |
|
||||
), |
|
||||
_commentWidget(context), |
|
||||
SizedBox( |
|
||||
height: 20, |
|
||||
), |
|
||||
Container( |
|
||||
height: 30, |
|
||||
child: InkWell( |
|
||||
onTap: () => sharing(), |
|
||||
child: Icon( |
|
||||
Icons.share, |
|
||||
size: 30, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
SizedBox( |
|
||||
height: 20, |
|
||||
), |
|
||||
_deleteWidget(context), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
_nameDescription(context), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget _buildVideoPlayer() { |
|
||||
return BlocProvider<VideoPlayerBloc>( |
|
||||
create: (context) => VideoPlayerBloc( |
|
||||
RepositoryProvider.of<VideoControllerService>(context)) |
|
||||
..add(VideoSelectedEvent(widget.postedAd)), |
|
||||
child: BlocBuilder<VideoPlayerBloc, VideoPlayerState>( |
|
||||
builder: (context, state) { |
|
||||
return Container(child: _getPlayer(context, state)); |
|
||||
}, |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget _getPlayer(BuildContext context, VideoPlayerState state) { |
|
||||
// final screenWidth = MediaQuery.of(context).size.width; |
|
||||
// final containerHeight = screenWidth / ASPECT_RATIO; |
|
||||
if (state is VideoPlayerStateLoaded) { |
|
||||
return GestureDetector( |
|
||||
onDoubleTap: () { |
|
||||
if (widget.postedAd.campaignID != null) { |
|
||||
if (favoured) { |
|
||||
return null; |
|
||||
} else { |
|
||||
likePost(); |
|
||||
} |
|
||||
} else { |
|
||||
if (favoured) { |
|
||||
dislikePost(); |
|
||||
} else { |
|
||||
likePost(); |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
child: Stack( |
|
||||
children: [ |
|
||||
VideoPlayerWidget( |
|
||||
key: Key(state.video.playbackID), |
|
||||
controller: state.controller, |
|
||||
ad: widget.postedAd, |
|
||||
), |
|
||||
AnimatedOpacity( |
|
||||
opacity: likeShow ? 1 : 0, |
|
||||
duration: Duration(seconds: 2), |
|
||||
onEnd: () { |
|
||||
setState(() { |
|
||||
likeShow = false; |
|
||||
}); |
|
||||
}, |
|
||||
child: Container( |
|
||||
width: double.infinity, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
child: Center( |
|
||||
child: Image.asset("assets/lovw.gif"), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
if (state is VideoPlayerStateLoading) { |
|
||||
return Container(); |
|
||||
} |
|
||||
|
|
||||
if (state is VideoPlayerStateError) { |
|
||||
return Container(); |
|
||||
} |
|
||||
|
|
||||
return Container(); |
|
||||
} |
|
||||
|
|
||||
Widget _favoriteWidget(BuildContext context) { |
|
||||
return Container( |
|
||||
height: 50, |
|
||||
child: InkWell( |
|
||||
onTap: () { |
|
||||
if (widget.postedAd.campaignID != null) { |
|
||||
if (favoured) { |
|
||||
return null; |
|
||||
} else { |
|
||||
likePost(); |
|
||||
} |
|
||||
} else { |
|
||||
if (favoured) { |
|
||||
dislikePost(); |
|
||||
} else { |
|
||||
likePost(); |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
child: new Wrap( |
|
||||
direction: Axis.vertical, |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: 50, |
|
||||
height: 30, |
|
||||
child: Center( |
|
||||
child: Icon( |
|
||||
Icons.favorite, |
|
||||
size: 30, |
|
||||
color: favoured ? Colors.red : Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
height: 20, |
|
||||
width: 50, |
|
||||
child: Center( |
|
||||
child: Text( |
|
||||
Numeral(widget.postedAd.likes).value().toString(), |
|
||||
style: TextStyle( |
|
||||
fontWeight: FontWeight.bold, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget _commentWidget(BuildContext context) { |
|
||||
return Container( |
|
||||
height: 50, |
|
||||
child: InkWell( |
|
||||
onTap: () => commentsDialog(context), |
|
||||
child: new Wrap( |
|
||||
direction: Axis.vertical, |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: 50, |
|
||||
height: 30, |
|
||||
child: Center( |
|
||||
child: Icon( |
|
||||
Icons.comment, |
|
||||
size: 30, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
height: 20, |
|
||||
width: 50, |
|
||||
child: Center( |
|
||||
child: Text( |
|
||||
Numeral(widget.postedAd.comments).value().toString(), |
|
||||
style: TextStyle( |
|
||||
fontWeight: FontWeight.bold, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget _nameDescription(BuildContext context) { |
|
||||
return Align( |
|
||||
alignment: Alignment.bottomLeft, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: 10, |
|
||||
vertical: MediaQuery.of(context).size.height * 0.05, |
|
||||
), |
|
||||
child: new Wrap( |
|
||||
direction: Axis.vertical, |
|
||||
children: [ |
|
||||
new RichText( |
|
||||
maxLines: 5, |
|
||||
text: TextSpan( |
|
||||
text: userDoc != null |
|
||||
? userDoc["username"] != null |
|
||||
? "@" + userDoc["username"] |
|
||||
: "" |
|
||||
: "", |
|
||||
style: TextStyle( |
|
||||
color: Colors.white, |
|
||||
fontSize: SizeConfig.safeBlockHorizontal * 4.3, |
|
||||
fontWeight: FontWeight.bold, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
SizedBox(height: 5), |
|
||||
Container( |
|
||||
margin: EdgeInsets.only(bottom: 20), |
|
||||
width: MediaQuery.of(context).size.width * 0.7, |
|
||||
child: Text( |
|
||||
widget.postedAd.title != null ? widget.postedAd.title : "", |
|
||||
style: TextStyle( |
|
||||
color: Colors.white, |
|
||||
fontSize: SizeConfig.safeBlockHorizontal * 4.3, |
|
||||
height: 1.5, |
|
||||
), |
|
||||
maxLines: 4, |
|
||||
overflow: TextOverflow.ellipsis, |
|
||||
textDirection: TextDirection.rtl, |
|
||||
textAlign: TextAlign.left, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget _deleteWidget(BuildContext context) { |
|
||||
return Container( |
|
||||
height: 30, |
|
||||
child: GestureDetector( |
|
||||
onTap: () async { |
|
||||
bool result = await Navigator.push( |
|
||||
context, |
|
||||
PageRouteBuilder( |
|
||||
opaque: false, |
|
||||
pageBuilder: (_, __, ___) => DeletePost(), |
|
||||
), |
|
||||
); |
|
||||
if (result) { |
|
||||
Provider.of<UserProvider>(context, listen: false).deletePost(Post( |
|
||||
aspect: widget.postedAd.aspect, |
|
||||
playbackID: widget.postedAd.playbackID, |
|
||||
postID: widget.postedAd.postID, |
|
||||
publisherID: widget.postedAd.publisherID, |
|
||||
assetID: widget.postedAd.assetID, |
|
||||
timestamp: widget.postedAd.timestamp, |
|
||||
title: widget.postedAd.title, |
|
||||
)); |
|
||||
Navigator.pop(context); |
|
||||
} |
|
||||
}, |
|
||||
child: Icon( |
|
||||
Icons.delete, |
|
||||
size: 28, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget _backWidget(BuildContext context) { |
|
||||
return GestureDetector( |
|
||||
onTap: () { |
|
||||
Navigator.pop(context); |
|
||||
}, |
|
||||
child: Container( |
|
||||
height: 40, |
|
||||
width: 40, |
|
||||
decoration: BoxDecoration( |
|
||||
color: Colors.transparent, |
|
||||
), |
|
||||
child: Icon( |
|
||||
Icons.arrow_back_ios, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,920 +0,0 @@ |
|||||
import 'dart:typed_data'; |
|
||||
import 'package:cached_network_image/cached_network_image.dart'; |
|
||||
import 'package:cloud_firestore/cloud_firestore.dart'; |
|
||||
import 'package:flare_flutter/flare_controls.dart'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter/services.dart'; |
|
||||
import 'package:flutter_bloc/flutter_bloc.dart'; |
|
||||
|
|
||||
import 'package:page_transition/page_transition.dart'; |
|
||||
import 'package:provider/provider.dart'; |
|
||||
import 'package:shared_preferences/shared_preferences.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/CouponDetails.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/Post.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/PostFav.dart'; |
|
||||
import 'package:teso/Classes/TesoUser.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/Posts/comment.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/userProfile3P.dart'; |
|
||||
import 'package:teso/Services/video_controller_service.dart'; |
|
||||
import 'package:teso/blocs/video_player/video_player_bloc.dart'; |
|
||||
import 'package:teso/blocs/video_player/video_player_event.dart'; |
|
||||
import 'package:teso/blocs/video_player/video_player_state.dart'; |
|
||||
import 'package:teso/providers/user_provider.dart'; |
|
||||
import 'package:teso/util/SizeConfig.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
import 'package:numeral/numeral.dart'; |
|
||||
import 'package:teso/GeneralWidgets/widgets/video_player_widget.dart'; |
|
||||
import 'package:http/http.dart' as http; |
|
||||
import 'dart:convert'; |
|
||||
|
|
||||
// ignore: must_be_immutable |
|
||||
class ViewPost extends StatefulWidget { |
|
||||
Post postedAd; |
|
||||
TesoUser user; |
|
||||
bool friend; |
|
||||
final bool play; |
|
||||
Function report; |
|
||||
|
|
||||
ViewPost({ |
|
||||
Key key, |
|
||||
this.postedAd, |
|
||||
this.user, |
|
||||
this.friend, |
|
||||
@required this.play, |
|
||||
@required this.report, |
|
||||
// this.posts, |
|
||||
}) : super(key: key); |
|
||||
@override |
|
||||
_ViewPostState createState() => _ViewPostState(); |
|
||||
} |
|
||||
|
|
||||
class _ViewPostState extends State<ViewPost> { |
|
||||
bool favoured = false; |
|
||||
List<CouponDetails> coupons = <CouponDetails>[]; |
|
||||
Uint8List imageBitmap; |
|
||||
final FlareControls flareControls = FlareControls(); |
|
||||
bool campaignAd = false; |
|
||||
int likes = 0; |
|
||||
int comments = 0; |
|
||||
var userDoc; |
|
||||
var document; |
|
||||
bool likeShow = false; |
|
||||
bool dark = false; |
|
||||
|
|
||||
void sharing(Post ad) async { |
|
||||
await rootBundle |
|
||||
.load("assets/images/rawLogoOverlay.png") |
|
||||
.then((value) => setState(() { |
|
||||
imageBitmap = value.buffer.asUint8List(); |
|
||||
})); |
|
||||
Provider.of<UserProvider>(context, listen: false).downloadVideo( |
|
||||
ad.postID, ad.playbackID, ad.rendition, imageBitmap, context); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() { |
|
||||
rootBundle.evict("assets/images/rawLogoOverlay.png"); |
|
||||
super.dispose(); |
|
||||
} |
|
||||
|
|
||||
void likePost(Post ad) { |
|
||||
setState(() { |
|
||||
likeShow = true; |
|
||||
}); |
|
||||
SharedPreferences.getInstance().then((value) { |
|
||||
String cid = value.getString("id"); |
|
||||
PostFav liked = new PostFav(); |
|
||||
liked.admirerId = cid; |
|
||||
liked.countId = DateTime.now().toString() + "$cid"; |
|
||||
liked.timestamp = DateTime.now().toIso8601String(); |
|
||||
liked.postId = ad.postID; |
|
||||
|
|
||||
setState(() { |
|
||||
// ad.likes.add(liked); |
|
||||
likes++; |
|
||||
favoured = true; |
|
||||
}); |
|
||||
|
|
||||
flareControls.play("like"); |
|
||||
Provider.of<UserProvider>(context, listen: false).addLike(liked); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
void dislikePost(Post ad) { |
|
||||
setState(() { |
|
||||
favoured = false; |
|
||||
likes--; |
|
||||
}); |
|
||||
Provider.of<UserProvider>(context, listen: false).deleteLike(ad.postID); |
|
||||
} |
|
||||
|
|
||||
void commentsDialog(BuildContext context) { |
|
||||
if (userDoc == null) { |
|
||||
FirebaseFirestore.instance |
|
||||
.collection("users") |
|
||||
.doc(widget.postedAd.publisherID) |
|
||||
.get() |
|
||||
.then((value) { |
|
||||
setState(() { |
|
||||
userDoc = value.data(); |
|
||||
}); |
|
||||
}); |
|
||||
} else { |
|
||||
showModalBottomSheet( |
|
||||
context: context, |
|
||||
shape: RoundedRectangleBorder( |
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20.0)), |
|
||||
), |
|
||||
builder: (BuildContext bc) { |
|
||||
return ClipRRect( |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topLeft: Radius.circular(20.0), |
|
||||
topRight: Radius.circular(20.0), |
|
||||
), |
|
||||
child: CommentSection( |
|
||||
postedAd: widget.postedAd, |
|
||||
user: TesoUser( |
|
||||
username: userDoc["username"], |
|
||||
userGUID: userDoc["id"], |
|
||||
)), |
|
||||
); |
|
||||
}, |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
Future<void> getCampaignCoupons(String campaign) async { |
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance(); |
|
||||
Map<String, String> requestHeaders = { |
|
||||
'Content-type': 'application/json', |
|
||||
'Authorization': prefs.getString('tokensTeso') |
|
||||
}; |
|
||||
try { |
|
||||
var register2 = serverLocation + 'coupons/campaign_coupons'; |
|
||||
var client1 = await http.post( |
|
||||
Uri.parse(register2), |
|
||||
headers: requestHeaders, |
|
||||
body: json.encode(campaign), |
|
||||
); |
|
||||
if (client1.statusCode == 200) { |
|
||||
var details = jsonDecode(client1.body); |
|
||||
setState(() { |
|
||||
coupons = List<CouponDetails>.from( |
|
||||
details.map((model) => CouponDetails.fromJSON(model)).toList()); |
|
||||
// coupons.removeWhere( |
|
||||
// (element) => element.expiration.isAfter(DateTime.now())); |
|
||||
}); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
print(e); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
campaignAd = false; |
|
||||
_getDocuments(); |
|
||||
_likedListen(); |
|
||||
_commentsListen(); |
|
||||
FirebaseFirestore.instance |
|
||||
.collection("users") |
|
||||
.doc(widget.postedAd.publisherID) |
|
||||
.get() |
|
||||
.then((value) { |
|
||||
if (mounted) |
|
||||
setState(() { |
|
||||
userDoc = value.data(); |
|
||||
}); |
|
||||
}); |
|
||||
SharedPreferences.getInstance().then((value) => |
|
||||
value.getString("theme") == "light" ? dark = false : dark = true); |
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
_getDocuments() { |
|
||||
FirebaseFirestore.instance |
|
||||
.collection("posts") |
|
||||
.doc(widget.postedAd.postID) |
|
||||
.get() |
|
||||
.then((value) { |
|
||||
if (mounted) |
|
||||
setState(() { |
|
||||
document = value.data(); |
|
||||
if (document != null) { |
|
||||
if (document["campaignId"] != null) { |
|
||||
campaignAd = true; |
|
||||
getCampaignCoupons(document["campaignId"]); |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
_likedListen() { |
|
||||
SharedPreferences.getInstance().then((value) { |
|
||||
String cid = value.getString("id"); |
|
||||
FirebaseFirestore.instance |
|
||||
.collection("posts") |
|
||||
.doc(widget.postedAd.postID) |
|
||||
.collection("likes") |
|
||||
.snapshots() |
|
||||
.listen((event) { |
|
||||
if (mounted) { |
|
||||
setState(() { |
|
||||
favoured = |
|
||||
event.docs.any((element) => element.data()["admirerID"] == cid); |
|
||||
likes = event.docs.length; |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
_commentsListen() { |
|
||||
FirebaseFirestore.instance |
|
||||
.collection("posts") |
|
||||
.doc(widget.postedAd.postID) |
|
||||
.collection("comments") |
|
||||
.snapshots() |
|
||||
.listen((event) { |
|
||||
if (mounted) { |
|
||||
setState(() { |
|
||||
comments = event.docs.length; |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
SizeConfig().init(context); |
|
||||
return Scaffold( |
|
||||
body: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
color: Colors.black, |
|
||||
child: Stack( |
|
||||
children: [ |
|
||||
_buildVideoPlayer(widget.postedAd), |
|
||||
Align( |
|
||||
alignment: Alignment.bottomRight, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.only( |
|
||||
right: 10, |
|
||||
bottom: 30, |
|
||||
), |
|
||||
width: 50, |
|
||||
height: MediaQuery.of(context).size.width * 0.73, |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
_publisherWidget(context), |
|
||||
SizedBox( |
|
||||
height: 20, |
|
||||
), |
|
||||
_favoriteWidget(context), |
|
||||
SizedBox( |
|
||||
height: 20, |
|
||||
), |
|
||||
_commentWidget(context), |
|
||||
SizedBox( |
|
||||
height: 20, |
|
||||
), |
|
||||
Container( |
|
||||
height: 30, |
|
||||
child: InkWell( |
|
||||
onTap: () => moreDialog(context, widget.postedAd), |
|
||||
child: Icon( |
|
||||
Icons.more_horiz, |
|
||||
size: 30, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
_nameDescription(context), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget _buildVideoPlayer(Post ad) { |
|
||||
return BlocProvider<VideoPlayerBloc>( |
|
||||
create: (context) => VideoPlayerBloc( |
|
||||
RepositoryProvider.of<VideoControllerService>(context)) |
|
||||
..add(VideoSelectedEvent(ad)), |
|
||||
child: BlocBuilder<VideoPlayerBloc, VideoPlayerState>( |
|
||||
builder: (context, state) { |
|
||||
return Container(child: _getPlayer(context, state, ad)); |
|
||||
}, |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget _getPlayer(BuildContext context, VideoPlayerState state, Post ad) { |
|
||||
// final screenWidth = MediaQuery.of(context).size.width; |
|
||||
// final containerHeight = screenWidth / ASPECT_RATIO; |
|
||||
final containerHeight = MediaQuery.of(context).size.height; |
|
||||
if (state is VideoPlayerStateLoaded) { |
|
||||
return GestureDetector( |
|
||||
onDoubleTap: () { |
|
||||
if (campaignAd) { |
|
||||
if (favoured) { |
|
||||
return null; |
|
||||
} else { |
|
||||
likePost(ad); |
|
||||
} |
|
||||
} else { |
|
||||
if (favoured) { |
|
||||
dislikePost(ad); |
|
||||
} else { |
|
||||
likePost(ad); |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
child: Stack( |
|
||||
children: [ |
|
||||
VideoPlayerWidget( |
|
||||
key: Key(state.video.playbackID), |
|
||||
controller: state.controller, |
|
||||
ad: ad, |
|
||||
play: widget.play, |
|
||||
details: coupons, |
|
||||
), |
|
||||
AnimatedOpacity( |
|
||||
opacity: likeShow ? 1 : 0, |
|
||||
duration: Duration(seconds: 2), |
|
||||
onEnd: () { |
|
||||
setState(() { |
|
||||
likeShow = false; |
|
||||
}); |
|
||||
}, |
|
||||
child: Container( |
|
||||
width: double.infinity, |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
child: Center( |
|
||||
child: Image.asset("assets/lovw.gif"), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
if (state is VideoPlayerStateLoading) { |
|
||||
return Container(); |
|
||||
} |
|
||||
|
|
||||
if (state is VideoPlayerStateError) { |
|
||||
return Container( |
|
||||
height: containerHeight, |
|
||||
color: Colors.black, |
|
||||
child: Center( |
|
||||
child: Text(state.message), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
return Container(); |
|
||||
} |
|
||||
|
|
||||
Widget _nameDescription(BuildContext context) { |
|
||||
return Align( |
|
||||
alignment: Alignment.bottomLeft, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: 10, |
|
||||
vertical: MediaQuery.of(context).size.height * 0.05, |
|
||||
), |
|
||||
child: new Wrap( |
|
||||
direction: Axis.vertical, |
|
||||
children: [ |
|
||||
new RichText( |
|
||||
maxLines: 5, |
|
||||
text: TextSpan( |
|
||||
text: userDoc != null ? "@" + userDoc["username"] : "", |
|
||||
style: TextStyle( |
|
||||
color: Colors.white, |
|
||||
fontSize: SizeConfig.safeBlockHorizontal * 4.3, |
|
||||
fontWeight: FontWeight.bold, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
SizedBox(height: 5), |
|
||||
Container( |
|
||||
margin: EdgeInsets.only(bottom: 20), |
|
||||
width: MediaQuery.of(context).size.width * 0.7, |
|
||||
child: Text( |
|
||||
document != null |
|
||||
? document["title"] != null |
|
||||
? document["title"] |
|
||||
: "" |
|
||||
: "", |
|
||||
style: TextStyle( |
|
||||
color: Colors.white, |
|
||||
fontSize: SizeConfig.safeBlockHorizontal * 4.3, |
|
||||
height: 1.5, |
|
||||
), |
|
||||
maxLines: 4, |
|
||||
overflow: TextOverflow.ellipsis, |
|
||||
textDirection: TextDirection.rtl, |
|
||||
textAlign: TextAlign.left, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget _publisherWidget(BuildContext context) { |
|
||||
return Container( |
|
||||
width: 40, |
|
||||
height: 40, |
|
||||
decoration: BoxDecoration( |
|
||||
shape: BoxShape.circle, |
|
||||
border: Border.all( |
|
||||
color: Colors.black, |
|
||||
width: 1, |
|
||||
), |
|
||||
), |
|
||||
child: ClipRRect( |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topLeft: Radius.circular(90.0), |
|
||||
topRight: Radius.circular(90.0), |
|
||||
bottomLeft: Radius.circular(90), |
|
||||
bottomRight: Radius.circular(90), |
|
||||
), |
|
||||
child: InkWell( |
|
||||
onTap: widget.postedAd.publisherID != null |
|
||||
? () => Navigator.pushReplacement( |
|
||||
context, |
|
||||
PageTransition( |
|
||||
child: UserProfileThirdPerson( |
|
||||
user: new TesoUser( |
|
||||
username: userDoc["username"], |
|
||||
userGUID: userDoc["id"], |
|
||||
firstname: userDoc["firstname"], |
|
||||
lastname: userDoc["surname"], |
|
||||
), |
|
||||
), |
|
||||
type: PageTransitionType.fade, |
|
||||
), |
|
||||
) |
|
||||
: null, |
|
||||
child: CachedNetworkImage( |
|
||||
imageUrl: |
|
||||
serverLocation + "api/pulldp/" + widget.postedAd.publisherID, |
|
||||
imageBuilder: (context, imageProvider) => FadeInImage( |
|
||||
height: 90, |
|
||||
width: 90, |
|
||||
fit: BoxFit.fill, |
|
||||
image: imageProvider, |
|
||||
placeholder: AssetImage("assets/images/tesoDP/dp1.png"), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget _favoriteWidget(BuildContext context) { |
|
||||
return Container( |
|
||||
height: 50, |
|
||||
child: InkWell( |
|
||||
onTap: () { |
|
||||
if (campaignAd) { |
|
||||
if (favoured) { |
|
||||
return null; |
|
||||
} else { |
|
||||
likePost(widget.postedAd); |
|
||||
} |
|
||||
} else { |
|
||||
if (favoured) { |
|
||||
dislikePost(widget.postedAd); |
|
||||
} else { |
|
||||
likePost(widget.postedAd); |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
child: new Wrap( |
|
||||
direction: Axis.vertical, |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: 50, |
|
||||
height: 30, |
|
||||
child: Center( |
|
||||
child: Icon( |
|
||||
Icons.favorite, |
|
||||
size: 30, |
|
||||
color: favoured ? Colors.red : Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
height: 20, |
|
||||
width: 50, |
|
||||
child: Center( |
|
||||
child: Text( |
|
||||
Numeral(likes).value().toString(), |
|
||||
style: TextStyle( |
|
||||
fontWeight: FontWeight.bold, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget _commentWidget(BuildContext context) { |
|
||||
return Container( |
|
||||
height: 50, |
|
||||
child: InkWell( |
|
||||
onTap: () => commentsDialog(context), |
|
||||
child: new Wrap( |
|
||||
direction: Axis.vertical, |
|
||||
children: [ |
|
||||
Container( |
|
||||
width: 50, |
|
||||
height: 30, |
|
||||
child: Center( |
|
||||
child: Icon( |
|
||||
Icons.comment, |
|
||||
size: 30, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
height: 20, |
|
||||
width: 50, |
|
||||
child: Center( |
|
||||
child: Text( |
|
||||
Numeral(comments).value().toString(), |
|
||||
style: TextStyle( |
|
||||
fontWeight: FontWeight.bold, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
void moreDialog(BuildContext context, Post ad) { |
|
||||
showModalBottomSheet( |
|
||||
context: context, |
|
||||
shape: RoundedRectangleBorder( |
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), |
|
||||
), |
|
||||
builder: (BuildContext bc) { |
|
||||
return Container( |
|
||||
child: SingleChildScrollView( |
|
||||
scrollDirection: Axis.vertical, |
|
||||
child: new Wrap( |
|
||||
children: <Widget>[ |
|
||||
new Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
vertical: 15.0, |
|
||||
), |
|
||||
child: Center( |
|
||||
child: Padding( |
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20.0), |
|
||||
child: Container( |
|
||||
width: 50, |
|
||||
height: 4, |
|
||||
color: Colors.grey, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
buildTop3(context, ad), |
|
||||
new Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: 40, |
|
||||
margin: EdgeInsets.symmetric(vertical: 20.0, horizontal: 15), |
|
||||
decoration: BoxDecoration( |
|
||||
color: !dark ? Colors.grey[200] : Colors.white12, |
|
||||
borderRadius: BorderRadius.circular(5)), |
|
||||
child: new Center( |
|
||||
child: Text( |
|
||||
"Why you're seeing this post", |
|
||||
textAlign: TextAlign.center, |
|
||||
style: TextStyle( |
|
||||
color: !dark ? Colors.black : Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
// bottomButtons(context, friends), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
}, |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
buildTop3(BuildContext context, Post ad) { |
|
||||
return Row( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround, |
|
||||
children: [ |
|
||||
GestureDetector( |
|
||||
onTap: () => sharing(ad), |
|
||||
child: Container( |
|
||||
width: SizeConfig.blockSizeHorizontal * 30, |
|
||||
height: SizeConfig.blockSizeHorizontal * 20, |
|
||||
decoration: BoxDecoration( |
|
||||
borderRadius: BorderRadius.all( |
|
||||
Radius.circular( |
|
||||
10, |
|
||||
), |
|
||||
), |
|
||||
color: !dark ? Colors.grey[200] : Colors.white12, |
|
||||
), |
|
||||
alignment: Alignment.center, |
|
||||
child: Center( |
|
||||
child: Column( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround, |
|
||||
children: [ |
|
||||
Icon(Icons.share), |
|
||||
Text( |
|
||||
"Share", |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
GestureDetector( |
|
||||
onTap: () => reportDialog(context), |
|
||||
child: Container( |
|
||||
width: SizeConfig.blockSizeHorizontal * 30, |
|
||||
height: SizeConfig.blockSizeHorizontal * 20, |
|
||||
decoration: BoxDecoration( |
|
||||
borderRadius: BorderRadius.all( |
|
||||
Radius.circular( |
|
||||
10, |
|
||||
), |
|
||||
), |
|
||||
color: !dark ? Colors.grey[200] : Colors.white12, |
|
||||
), |
|
||||
alignment: Alignment.center, |
|
||||
child: Center( |
|
||||
child: Column( |
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround, |
|
||||
children: [ |
|
||||
Icon( |
|
||||
Icons.report_problem, |
|
||||
color: Colors.red[900], |
|
||||
), |
|
||||
Text( |
|
||||
"Report", |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
flagContent(level) { |
|
||||
Provider.of<UserProvider>(context, listen: false) |
|
||||
.flagPost(widget.postedAd, level); |
|
||||
Navigator.pop(context); |
|
||||
widget.report(widget.postedAd); |
|
||||
this.dispose(); |
|
||||
} |
|
||||
|
|
||||
bottomButtons(BuildContext context, friends) { |
|
||||
return new Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
margin: EdgeInsets.symmetric(vertical: 5.0, horizontal: 15), |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
new Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: new Center( |
|
||||
child: ElevatedButton( |
|
||||
style: ElevatedButton.styleFrom( |
|
||||
primary: Colors.grey[200], |
|
||||
shape: RoundedRectangleBorder( |
|
||||
borderRadius: BorderRadius.all( |
|
||||
Radius.circular(5.0), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
onPressed: () => Navigator.pop(context), |
|
||||
child: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: Text( |
|
||||
"Hide", |
|
||||
textAlign: TextAlign.center, |
|
||||
)), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
friends |
|
||||
? new Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: new Center( |
|
||||
child: ElevatedButton( |
|
||||
style: ElevatedButton.styleFrom( |
|
||||
primary: Colors.grey[300], |
|
||||
shape: RoundedRectangleBorder( |
|
||||
borderRadius: BorderRadius.all( |
|
||||
Radius.circular(5.0), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
onPressed: () => Navigator.pop(context), |
|
||||
child: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: Text( |
|
||||
"Unfrend", |
|
||||
textAlign: TextAlign.center, |
|
||||
)), |
|
||||
), |
|
||||
), |
|
||||
) |
|
||||
: new Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: new Center( |
|
||||
child: ElevatedButton( |
|
||||
style: ElevatedButton.styleFrom( |
|
||||
primary: Colors.grey[300], |
|
||||
shape: RoundedRectangleBorder( |
|
||||
borderRadius: BorderRadius.all( |
|
||||
Radius.circular(5.0), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
onPressed: () => Navigator.pop(context), |
|
||||
child: Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: Text( |
|
||||
"Add Friend", |
|
||||
textAlign: TextAlign.center, |
|
||||
)), |
|
||||
), |
|
||||
), |
|
||||
) |
|
||||
], |
|
||||
)); |
|
||||
} |
|
||||
|
|
||||
void reportDialog(BuildContext context) { |
|
||||
showModalBottomSheet( |
|
||||
context: context, |
|
||||
isScrollControlled: true, |
|
||||
enableDrag: true, |
|
||||
shape: RoundedRectangleBorder( |
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), |
|
||||
), |
|
||||
builder: (BuildContext bc) { |
|
||||
return Container( |
|
||||
height: MediaQuery.of(context).size.height * 0.95, |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
new Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
vertical: 15.0, |
|
||||
), |
|
||||
child: Center( |
|
||||
child: Padding( |
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20.0), |
|
||||
child: Container( |
|
||||
width: 50, |
|
||||
height: 4, |
|
||||
color: Colors.grey, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Container( |
|
||||
child: Center( |
|
||||
child: Text( |
|
||||
"Report", |
|
||||
style: TextStyle( |
|
||||
fontSize: SizeConfig.blockSizeHorizontal * 3.5, |
|
||||
fontWeight: FontWeight.w800, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Divider(), |
|
||||
Container( |
|
||||
padding: EdgeInsets.symmetric(horizontal: 10), |
|
||||
child: Text( |
|
||||
"Why are you reporting this post?", |
|
||||
textAlign: TextAlign.left, |
|
||||
style: TextStyle( |
|
||||
fontSize: SizeConfig.blockSizeHorizontal * 3.5, |
|
||||
fontWeight: FontWeight.w800, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
SizedBox( |
|
||||
height: 5, |
|
||||
), |
|
||||
Container( |
|
||||
padding: EdgeInsets.symmetric(horizontal: 15), |
|
||||
child: Center( |
|
||||
child: Text( |
|
||||
"Your report would be handled as soon as possible. However if someone is in immediate danger, call the local emergency services - don't wait.", |
|
||||
style: TextStyle( |
|
||||
fontSize: SizeConfig.blockSizeHorizontal * 3.5, |
|
||||
fontWeight: FontWeight.w400, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
Divider(), |
|
||||
Container( |
|
||||
height: MediaQuery.of(context).size.height * 0.7, |
|
||||
child: new ListView( |
|
||||
scrollDirection: Axis.vertical, |
|
||||
children: <Widget>[ |
|
||||
ListTile( |
|
||||
title: Text("It's a spam"), |
|
||||
trailing: Icon(Icons.arrow_forward_ios), |
|
||||
onTap: () => flagContent(1), |
|
||||
), |
|
||||
ListTile( |
|
||||
title: Text("Nudity or sexual activity"), |
|
||||
trailing: Icon(Icons.arrow_forward_ios), |
|
||||
onTap: () => flagContent(2), |
|
||||
), |
|
||||
ListTile( |
|
||||
title: Text("I just don't like it"), |
|
||||
trailing: Icon(Icons.arrow_forward_ios), |
|
||||
onTap: () => flagContent(3), |
|
||||
), |
|
||||
ListTile( |
|
||||
title: Text("Hate speech or symbols"), |
|
||||
trailing: Icon(Icons.arrow_forward_ios), |
|
||||
onTap: () => flagContent(4), |
|
||||
), |
|
||||
ListTile( |
|
||||
title: Text("False information"), |
|
||||
trailing: Icon(Icons.arrow_forward_ios), |
|
||||
onTap: () => flagContent(5), |
|
||||
), |
|
||||
ListTile( |
|
||||
title: Text("Bullying harassment"), |
|
||||
trailing: Icon(Icons.arrow_forward_ios), |
|
||||
onTap: () => flagContent(6), |
|
||||
), |
|
||||
ListTile( |
|
||||
title: Text("Violence or dangerous organisations"), |
|
||||
trailing: Icon(Icons.arrow_forward_ios), |
|
||||
onTap: () => flagContent(7), |
|
||||
), |
|
||||
ListTile( |
|
||||
title: Text("Scam or fraud"), |
|
||||
trailing: Icon(Icons.arrow_forward_ios), |
|
||||
onTap: () => flagContent(8), |
|
||||
), |
|
||||
ListTile( |
|
||||
title: Text("Intellectual property violation"), |
|
||||
trailing: Icon(Icons.arrow_forward_ios), |
|
||||
onTap: () => flagContent(9), |
|
||||
), |
|
||||
ListTile( |
|
||||
title: Text("Sale of illegal or regulated goods"), |
|
||||
trailing: Icon(Icons.arrow_forward_ios), |
|
||||
onTap: () => flagContent(10), |
|
||||
), |
|
||||
ListTile( |
|
||||
title: Text("Suicide or self-injury"), |
|
||||
trailing: Icon(Icons.arrow_forward_ios), |
|
||||
onTap: () => flagContent(11), |
|
||||
), |
|
||||
ListTile( |
|
||||
title: Text("Eating disorders"), |
|
||||
trailing: Icon(Icons.arrow_forward_ios), |
|
||||
onTap: () => flagContent(12), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
); |
|
||||
}, |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,332 +0,0 @@ |
|||||
import 'package:teso/Classes/API%20Clasess/Post.dart'; |
|
||||
import 'package:teso/Classes/Firebase/Comments.dart'; |
|
||||
import 'package:teso/Classes/TesoUser.dart'; |
|
||||
import 'package:teso/Pages/PageWidgets/Posts/user3P_commentTitle.dart'; |
|
||||
import 'package:cloud_firestore/cloud_firestore.dart'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:provider/provider.dart'; |
|
||||
import 'package:shared_preferences/shared_preferences.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/CommentsPost.dart'; |
|
||||
import 'package:teso/Pages/PageWidgets/ChatScreen/bottomBar.dart'; |
|
||||
import 'package:teso/providers/user_provider.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
import 'package:intl/intl.dart'; |
|
||||
import 'package:time_elapsed/time_elapsed.dart'; |
|
||||
|
|
||||
class CommentSection extends StatefulWidget { |
|
||||
final Post postedAd; |
|
||||
final TesoUser user; |
|
||||
CommentSection({ |
|
||||
Key key, |
|
||||
@required this.postedAd, |
|
||||
@required this.user, |
|
||||
}) : assert(postedAd != null), |
|
||||
assert(user != null), |
|
||||
super(key: key); |
|
||||
@override |
|
||||
_CommentSectionState createState() => _CommentSectionState(); |
|
||||
} |
|
||||
|
|
||||
class _CommentSectionState extends State<CommentSection> { |
|
||||
TextEditingController controller = new TextEditingController(); |
|
||||
final ScrollController listScrollController = ScrollController(); |
|
||||
List<FBComments> listMessage = <FBComments>[]; |
|
||||
int _limit = 20; |
|
||||
final int _limitIncrement = 20; |
|
||||
int total = 0; |
|
||||
var userDocs; |
|
||||
List<FBComments> latest = <FBComments>[]; |
|
||||
|
|
||||
_scrollListener() { |
|
||||
if (listScrollController.offset >= |
|
||||
listScrollController.position.maxScrollExtent && |
|
||||
!listScrollController.position.outOfRange) { |
|
||||
print("reach the bottom"); |
|
||||
setState(() { |
|
||||
print("reach the bottom"); |
|
||||
_limit += _limitIncrement; |
|
||||
}); |
|
||||
} |
|
||||
if (listScrollController.offset <= |
|
||||
listScrollController.position.minScrollExtent && |
|
||||
!listScrollController.position.outOfRange) { |
|
||||
print("reach the top"); |
|
||||
setState(() { |
|
||||
print("reach the top"); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
super.initState(); |
|
||||
listScrollController.addListener(_scrollListener); |
|
||||
_listenComments(); |
|
||||
FirebaseFirestore.instance |
|
||||
.collection("users") |
|
||||
.doc(widget.postedAd.publisherID) |
|
||||
.get() |
|
||||
.then((value) { |
|
||||
setState(() { |
|
||||
userDocs = value.data(); |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() { |
|
||||
controller.dispose(); |
|
||||
listScrollController.dispose(); |
|
||||
super.dispose(); |
|
||||
} |
|
||||
|
|
||||
void sendComment(String text, int position) async { |
|
||||
TesoUser user = |
|
||||
Provider.of<UserProvider>(context, listen: false).currentUser; |
|
||||
if (controller.text.isNotEmpty) { |
|
||||
CommentsPost comment = new CommentsPost(); |
|
||||
SharedPreferences.getInstance().then((value) async { |
|
||||
comment.postId = widget.postedAd.postID; |
|
||||
comment.comment = text.trim(); |
|
||||
comment.timestamp = DateTime.now().toIso8601String(); |
|
||||
comment.commenterId = value.getString("id"); |
|
||||
comment.commentId = |
|
||||
"TESCPCM" + DateTime.now().toString() + value.getString("id"); |
|
||||
|
|
||||
setState(() { |
|
||||
latest.add(FBComments( |
|
||||
comment: comment.comment, |
|
||||
commenterID: comment.commenterId, |
|
||||
commentID: comment.commentId, |
|
||||
commenter: user.username, |
|
||||
postID: comment.postId, |
|
||||
thumbnail: user.thumbnail_dp, |
|
||||
timestamp: DateTime.now(), |
|
||||
)); |
|
||||
total++; |
|
||||
}); |
|
||||
}); |
|
||||
Provider.of<UserProvider>(context, listen: false).commentPost(comment); |
|
||||
controller.clear(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
_listenComments() { |
|
||||
FirebaseFirestore.instance |
|
||||
.collection('posts') |
|
||||
.doc(widget.postedAd.postID) |
|
||||
.collection("comments") |
|
||||
.orderBy('timestamp') |
|
||||
.snapshots() |
|
||||
.listen((event) { |
|
||||
if (mounted) { |
|
||||
setState(() { |
|
||||
total = event.docs.length; |
|
||||
latest.clear(); |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Scaffold( |
|
||||
appBar: AppBar( |
|
||||
automaticallyImplyLeading: false, |
|
||||
centerTitle: true, |
|
||||
title: Text( |
|
||||
"$total comments", |
|
||||
style: TextStyle( |
|
||||
fontSize: 14, |
|
||||
), |
|
||||
), |
|
||||
actions: [ |
|
||||
IconButton( |
|
||||
onPressed: () => Navigator.pop(context), |
|
||||
icon: Icon( |
|
||||
Icons.close, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
body: Container( |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: Stack( |
|
||||
children: [ |
|
||||
Container( |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
margin: EdgeInsets.only(bottom: 60), |
|
||||
child: StreamBuilder( |
|
||||
stream: FirebaseFirestore.instance |
|
||||
.collection('posts') |
|
||||
.doc(widget.postedAd.postID) |
|
||||
.collection("comments") |
|
||||
.orderBy('timestamp') |
|
||||
.limit(_limit) |
|
||||
.snapshots(), |
|
||||
builder: (context, snapshot) { |
|
||||
if (snapshot.data == null && |
|
||||
snapshot.connectionState == ConnectionState.waiting) { |
|
||||
return Center( |
|
||||
child: CircularProgressIndicator( |
|
||||
valueColor: AlwaysStoppedAnimation<Color>( |
|
||||
Theme.of(context).primaryColor))); |
|
||||
} else if (snapshot.data.docs.length == 0) { |
|
||||
if (widget.postedAd.title != null) { |
|
||||
return Align( |
|
||||
alignment: Alignment.topCenter, |
|
||||
child: buildPostTile3P( |
|
||||
context, widget.user, widget.postedAd), |
|
||||
); |
|
||||
} else { |
|
||||
return Container(); |
|
||||
} |
|
||||
} else { |
|
||||
QuerySnapshot results = snapshot.data; |
|
||||
listMessage = results.docs |
|
||||
.map((e) => FBComments.fromJSON(e.data())) |
|
||||
.toList(); |
|
||||
if (latest.length != 0) { |
|
||||
listMessage.addAll(latest); |
|
||||
} |
|
||||
|
|
||||
return ListView.builder( |
|
||||
padding: EdgeInsets.all(10.0), |
|
||||
itemCount: listMessage.length, |
|
||||
itemBuilder: (context, index) { |
|
||||
var formattedDate = DateFormat("yyyy-MM-dd HH:mm:ss") |
|
||||
.format(listMessage[index].timestamp); |
|
||||
if (index == 0 && widget.postedAd.title != null) { |
|
||||
return Column(children: [ |
|
||||
buildPostTile3P( |
|
||||
context, widget.user, widget.postedAd), |
|
||||
Divider(), |
|
||||
ListTile( |
|
||||
leading: Container( |
|
||||
width: 40, |
|
||||
height: 40, |
|
||||
decoration: BoxDecoration( |
|
||||
shape: BoxShape.circle, |
|
||||
border: Border.all( |
|
||||
color: Colors.black, |
|
||||
width: 1, |
|
||||
)), |
|
||||
child: ClipRRect( |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topLeft: Radius.circular(90.0), |
|
||||
topRight: Radius.circular(90.0), |
|
||||
bottomLeft: Radius.circular(90), |
|
||||
bottomRight: Radius.circular(90), |
|
||||
), |
|
||||
child: FadeInImage( |
|
||||
height: 90, |
|
||||
width: 90, |
|
||||
fit: BoxFit.fill, |
|
||||
image: NetworkImage(serverLocation + |
|
||||
"api/pulldp/" + |
|
||||
snapshot.data.docs[index] |
|
||||
.data()['commenterID']), |
|
||||
placeholder: AssetImage( |
|
||||
"assets/images/tesoDP/dp1.png"), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
title: new RichText( |
|
||||
text: new TextSpan( |
|
||||
style: new TextStyle( |
|
||||
fontSize: 14.0, |
|
||||
color: Theme.of(context).primaryColorLight, |
|
||||
), |
|
||||
children: <TextSpan>[ |
|
||||
new TextSpan( |
|
||||
text: snapshot.data.docs[index] |
|
||||
.data()['commenter'] + |
|
||||
" ", |
|
||||
style: new TextStyle( |
|
||||
fontWeight: FontWeight.bold)), |
|
||||
new TextSpan( |
|
||||
text: snapshot.data.docs[index] |
|
||||
.data()['comment']), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
subtitle: Text( |
|
||||
TimeElapsed.fromDateTime( |
|
||||
DateTime.parse(formattedDate)), |
|
||||
), |
|
||||
), |
|
||||
]); |
|
||||
} |
|
||||
return ListTile( |
|
||||
leading: Container( |
|
||||
width: 40, |
|
||||
height: 40, |
|
||||
decoration: BoxDecoration( |
|
||||
shape: BoxShape.circle, |
|
||||
border: Border.all( |
|
||||
color: Colors.black, |
|
||||
width: 1, |
|
||||
)), |
|
||||
child: ClipRRect( |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topLeft: Radius.circular(90.0), |
|
||||
topRight: Radius.circular(90.0), |
|
||||
bottomLeft: Radius.circular(90), |
|
||||
bottomRight: Radius.circular(90), |
|
||||
), |
|
||||
child: FadeInImage( |
|
||||
height: 90, |
|
||||
width: 90, |
|
||||
fit: BoxFit.fill, |
|
||||
image: NetworkImage(serverLocation + |
|
||||
"api/pulldp/" + |
|
||||
listMessage[index].commenterID), |
|
||||
placeholder: |
|
||||
AssetImage("assets/images/tesoDP/dp1.png"), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
title: new RichText( |
|
||||
text: new TextSpan( |
|
||||
style: new TextStyle( |
|
||||
fontSize: 14.0, |
|
||||
color: Theme.of(context).primaryColorLight, |
|
||||
), |
|
||||
children: <TextSpan>[ |
|
||||
new TextSpan( |
|
||||
text: listMessage[index].commenter + " ", |
|
||||
style: new TextStyle( |
|
||||
fontWeight: FontWeight.bold)), |
|
||||
new TextSpan(text: listMessage[index].comment), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
subtitle: Text( |
|
||||
TimeElapsed.fromDateTime( |
|
||||
DateTime.parse(formattedDate)), |
|
||||
), |
|
||||
); |
|
||||
}, |
|
||||
// reverse: true, |
|
||||
controller: listScrollController, |
|
||||
); |
|
||||
} |
|
||||
}, |
|
||||
), |
|
||||
), |
|
||||
Align( |
|
||||
alignment: Alignment.bottomCenter, |
|
||||
child: buildBottom( |
|
||||
context, |
|
||||
controller, |
|
||||
sendComment, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,155 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter/widgets.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/Post.dart'; |
|
||||
import 'package:teso/util/SizeConfig.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
|
|
||||
class DeletePost extends StatefulWidget { |
|
||||
final Post post; |
|
||||
const DeletePost({Key key, this.post}) : super(key: key); |
|
||||
|
|
||||
@override |
|
||||
_DeletePostState createState() => _DeletePostState(); |
|
||||
} |
|
||||
|
|
||||
class _DeletePostState extends State<DeletePost> { |
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
SizeConfig().init(context); |
|
||||
return Scaffold( |
|
||||
backgroundColor: Color.fromRGBO(0, 0, 0, 0.5), |
|
||||
body: Stack( |
|
||||
children: [ |
|
||||
Align( |
|
||||
alignment: Alignment.bottomCenter, |
|
||||
child: Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
vertical: SizeConfig.blockSizeHorizontal * 5, |
|
||||
), |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: SizeConfig.blockSizeHorizontal * 52.6, |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
_descriptionBox(context), |
|
||||
SizedBox( |
|
||||
height: 5, |
|
||||
), |
|
||||
_cancelDelete(context), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget _descriptionBox(BuildContext context) { |
|
||||
return Container( |
|
||||
decoration: BoxDecoration( |
|
||||
borderRadius: BorderRadius.circular(30.0), |
|
||||
), |
|
||||
child: ClipRRect( |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topLeft: Radius.circular(15), |
|
||||
topRight: Radius.circular(15), |
|
||||
bottomLeft: Radius.circular(15), |
|
||||
bottomRight: Radius.circular(15), |
|
||||
), |
|
||||
child: Material( |
|
||||
child: Container( |
|
||||
padding: EdgeInsets.all(10), |
|
||||
child: Column( |
|
||||
children: [ |
|
||||
SizedBox( |
|
||||
height: 5, |
|
||||
), |
|
||||
Container( |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
horizontal: SizeConfig.safeBlockHorizontal * 2), |
|
||||
padding: EdgeInsets.symmetric( |
|
||||
horizontal: SizeConfig.safeBlockHorizontal * 6), |
|
||||
alignment: Alignment.center, |
|
||||
child: Text( |
|
||||
"Once you proceed you cannot undo your actions . " + |
|
||||
"Are you sure you would like to delete this post ? ", |
|
||||
textAlign: TextAlign.center, |
|
||||
style: TextStyle( |
|
||||
color: Theme.of(context).colorScheme.secondary == |
|
||||
Color(0xFFfd0a35) |
|
||||
? Colors.white24 |
|
||||
: tesoBlue), |
|
||||
), |
|
||||
), |
|
||||
SizedBox( |
|
||||
height: 10, |
|
||||
), |
|
||||
Divider(), |
|
||||
SizedBox( |
|
||||
height: 10, |
|
||||
), |
|
||||
InkWell( |
|
||||
onTap: () => Navigator.pop(context, true), |
|
||||
child: Container( |
|
||||
alignment: Alignment.center, |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: Text( |
|
||||
"Delete", |
|
||||
style: TextStyle( |
|
||||
color: Colors.red, |
|
||||
fontSize: SizeConfig.safeBlockHorizontal * 4.0, |
|
||||
fontWeight: FontWeight.bold, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
SizedBox( |
|
||||
height: 10, |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
Widget _cancelDelete(BuildContext context) { |
|
||||
return Container( |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
height: SizeConfig.safeBlockVertical * 6.5, |
|
||||
decoration: BoxDecoration( |
|
||||
borderRadius: BorderRadius.circular(30.0), |
|
||||
), |
|
||||
child: ClipRRect( |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topLeft: Radius.circular(15), |
|
||||
topRight: Radius.circular(15), |
|
||||
bottomLeft: Radius.circular(15), |
|
||||
bottomRight: Radius.circular(15), |
|
||||
), |
|
||||
child: ColorFiltered( |
|
||||
colorFilter: ColorFilter.mode( |
|
||||
Colors.white.withOpacity(0.1), BlendMode.lighten), |
|
||||
child: Material( |
|
||||
child: Container( |
|
||||
padding: EdgeInsets.all(10), |
|
||||
child: InkWell( |
|
||||
onTap: () => Navigator.pop(context, false), |
|
||||
child: Text( |
|
||||
"Cancel", |
|
||||
textAlign: TextAlign.center, |
|
||||
style: TextStyle( |
|
||||
color: Colors.blueAccent, |
|
||||
fontSize: SizeConfig.safeBlockHorizontal * 4.5, |
|
||||
fontWeight: FontWeight.bold, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,103 +0,0 @@ |
|||||
// import 'dart:typed_data'; |
|
||||
|
|
||||
// import 'package:teso/Classes/API%20Clasess/PostedAd.dart'; |
|
||||
// import 'package:teso/Pages/PageWidgets/Posts/comment.dart'; |
|
||||
// import 'package:teso/Pages/PageWidgets/Posts/expandedPost.dart'; |
|
||||
// import 'package:flutter/material.dart'; |
|
||||
// |
|
||||
// import 'package:teso/util/consts.dart'; |
|
||||
// import 'package:video_player/video_player.dart'; |
|
||||
|
|
||||
// class PostDetails extends StatefulWidget { |
|
||||
// final PostedAd type; |
|
||||
// const PostDetails({Key key, this.type}) : super(key: key); |
|
||||
// @override |
|
||||
// _PostDetailsState createState() => _PostDetailsState(post: type); |
|
||||
// } |
|
||||
|
|
||||
// class _PostDetailsState extends State<PostDetails> { |
|
||||
// PostedAd post; |
|
||||
// _PostDetailsState({this.post}); |
|
||||
// bool available = false; |
|
||||
// Uint8List bytes; |
|
||||
// TextEditingController controller; |
|
||||
// VideoPlayerController _videoPlayerController; |
|
||||
|
|
||||
// void deleteDialog(context) { |
|
||||
// showModalBottomSheet( |
|
||||
// context: context, |
|
||||
// shape: RoundedRectangleBorder( |
|
||||
// borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), |
|
||||
// ), |
|
||||
// builder: (BuildContext bc) { |
|
||||
// return Container( |
|
||||
// child: new Wrap( |
|
||||
// children: <Widget>[ |
|
||||
// new Container( |
|
||||
// width: double.infinity, |
|
||||
// margin: EdgeInsets.only( |
|
||||
// top: 20.0, |
|
||||
// bottom: 12.0, |
|
||||
// ), |
|
||||
// child: Center( |
|
||||
// child: Text( |
|
||||
// "Delete post", |
|
||||
// style: TextStyle( |
|
||||
// fontSize: 12.0, |
|
||||
// ), |
|
||||
// ), |
|
||||
// )), |
|
||||
// Padding( |
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 20.0), |
|
||||
// child: Divider(), |
|
||||
// ), |
|
||||
// new ListTile( |
|
||||
// leading: new Icon(MaterialCommunityIcons.trash_can), |
|
||||
// title: new Text('Delete'), |
|
||||
// onTap: () => print("Delete")), |
|
||||
// ], |
|
||||
// ), |
|
||||
// ); |
|
||||
// }, |
|
||||
// ); |
|
||||
// } |
|
||||
|
|
||||
// @override |
|
||||
// void initState() { |
|
||||
// super.initState(); |
|
||||
// _videoPlayerController = |
|
||||
// VideoPlayerController.network(tesoStreaming + post.post.path) |
|
||||
// ..initialize().then((_) { |
|
||||
// _videoPlayerController.play(); |
|
||||
// _videoPlayerController.setLooping(true); |
|
||||
// setState(() {}); |
|
||||
// }); |
|
||||
// } |
|
||||
|
|
||||
// @override |
|
||||
// Widget build(BuildContext context) { |
|
||||
// return Scaffold( |
|
||||
// appBar: null, |
|
||||
// body: Container( |
|
||||
// margin: EdgeInsets.only( |
|
||||
// top: 50, |
|
||||
// left: 10, |
|
||||
// right: 10, |
|
||||
// ), |
|
||||
// height: MediaQuery.of(context).size.height, |
|
||||
// width: MediaQuery.of(context).size.width, |
|
||||
// child: SingleChildScrollView( |
|
||||
// scrollDirection: Axis.vertical, |
|
||||
// child: Column( |
|
||||
// children: [ |
|
||||
// buildPostDetails( |
|
||||
// context, post, deleteDialog, _videoPlayerController), |
|
||||
// Padding(padding: EdgeInsets.all(05)), |
|
||||
// buildCommentTile(context, available, bytes, controller), |
|
||||
// ], |
|
||||
// ), |
|
||||
// ), |
|
||||
// ), |
|
||||
// ); |
|
||||
// } |
|
||||
// } |
|
@ -1,289 +0,0 @@ |
|||||
import 'package:cloud_firestore/cloud_firestore.dart'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
import 'package:page_transition/page_transition.dart'; |
|
||||
import 'package:provider/provider.dart'; |
|
||||
import 'package:shared_preferences/shared_preferences.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/CommentsPost.dart'; |
|
||||
import 'package:teso/Classes/Firebase/Posts.dart'; |
|
||||
import 'package:teso/Pages/PageWidgets/ChatScreen/bottomBar.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/Posts/UserPosts.dart'; |
|
||||
import 'package:teso/providers/user_provider.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
import 'package:intl/intl.dart'; |
|
||||
import 'package:time_elapsed/time_elapsed.dart'; |
|
||||
|
|
||||
// ignore: must_be_immutable |
|
||||
class CommentSection extends StatefulWidget { |
|
||||
FBPosts postedAd; |
|
||||
CommentSection({ |
|
||||
Key key, |
|
||||
@required this.postedAd, |
|
||||
}) : assert(postedAd != null), |
|
||||
super(key: key); |
|
||||
@override |
|
||||
_CommentSectionState createState() => _CommentSectionState(); |
|
||||
} |
|
||||
|
|
||||
class _CommentSectionState extends State<CommentSection> { |
|
||||
TextEditingController controller = new TextEditingController(); |
|
||||
final ScrollController listScrollController = ScrollController(); |
|
||||
List<QueryDocumentSnapshot> listMessage = new List.from([]); |
|
||||
int _limit = 20; |
|
||||
final int _limitIncrement = 20; |
|
||||
|
|
||||
_scrollListener() { |
|
||||
if (listScrollController.offset >= |
|
||||
listScrollController.position.maxScrollExtent && |
|
||||
!listScrollController.position.outOfRange) { |
|
||||
print("reach the bottom"); |
|
||||
setState(() { |
|
||||
print("reach the bottom"); |
|
||||
_limit += _limitIncrement; |
|
||||
}); |
|
||||
} |
|
||||
if (listScrollController.offset <= |
|
||||
listScrollController.position.minScrollExtent && |
|
||||
!listScrollController.position.outOfRange) { |
|
||||
print("reach the top"); |
|
||||
setState(() { |
|
||||
print("reach the top"); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
super.initState(); |
|
||||
listScrollController.addListener(_scrollListener); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() { |
|
||||
controller.dispose(); |
|
||||
listScrollController.dispose(); |
|
||||
super.dispose(); |
|
||||
} |
|
||||
|
|
||||
void sendComment(String text, int position) async { |
|
||||
if (controller.text.isNotEmpty) { |
|
||||
CommentsPost comment = new CommentsPost(); |
|
||||
SharedPreferences.getInstance().then((value) async { |
|
||||
comment.postId = widget.postedAd.postID; |
|
||||
comment.comment = text; |
|
||||
comment.timestamp = DateTime.now().toIso8601String(); |
|
||||
comment.commenterId = value.getString("id"); |
|
||||
comment.commentId = |
|
||||
"TESCPCM" + DateTime.now().toString() + value.getString("id"); |
|
||||
}); |
|
||||
// setState(() { |
|
||||
// widget.postedAd.comments.add(comment); |
|
||||
// }); |
|
||||
|
|
||||
Provider.of<UserProvider>(context, listen: false).commentPost(comment); |
|
||||
controller.clear(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Scaffold( |
|
||||
appBar: AppBar( |
|
||||
title: Text("Comments"), |
|
||||
leading: IconButton( |
|
||||
icon: Icon( |
|
||||
Icons.arrow_back_ios, |
|
||||
), |
|
||||
onPressed: () => Navigator.pushReplacement( |
|
||||
context, |
|
||||
PageTransition( |
|
||||
child: UserPosts( |
|
||||
postedAd: widget.postedAd, |
|
||||
), |
|
||||
type: PageTransitionType.rightToLeft, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
body: Container( |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
child: Stack( |
|
||||
children: [ |
|
||||
Container( |
|
||||
height: MediaQuery.of(context).size.height, |
|
||||
width: MediaQuery.of(context).size.width, |
|
||||
margin: EdgeInsets.only(bottom: 60), |
|
||||
child: StreamBuilder( |
|
||||
stream: FirebaseFirestore.instance |
|
||||
.collection('posts') |
|
||||
.doc('comments') |
|
||||
.collection(widget.postedAd.postID) |
|
||||
.orderBy('timestamp') |
|
||||
.limit(_limit) |
|
||||
.snapshots(), |
|
||||
builder: (context, snapshot) { |
|
||||
if (snapshot.data == null && |
|
||||
snapshot.connectionState == ConnectionState.waiting) { |
|
||||
return Center( |
|
||||
child: CircularProgressIndicator( |
|
||||
valueColor: AlwaysStoppedAnimation<Color>( |
|
||||
Theme.of(context).primaryColor))); |
|
||||
} else if (snapshot.data.docs.length == 0) { |
|
||||
// if (widget.postedAd.title != null) { |
|
||||
// return Align( |
|
||||
// alignment: Alignment.topCenter, |
|
||||
// child: buildPostTile(context, widget.postedAd), |
|
||||
// ); |
|
||||
// } else { |
|
||||
return Container(); |
|
||||
// } |
|
||||
} else { |
|
||||
listMessage = snapshot.data.docs; |
|
||||
return ListView.builder( |
|
||||
padding: EdgeInsets.all(10.0), |
|
||||
itemCount: listMessage.length, |
|
||||
itemBuilder: (context, index) { |
|
||||
int timeInMillis = int.parse(snapshot.data.docs[index] |
|
||||
.data()['timestamp'] |
|
||||
.toString()); |
|
||||
var date = |
|
||||
DateTime.fromMillisecondsSinceEpoch(timeInMillis); |
|
||||
var formattedDate = |
|
||||
DateFormat("yyyy-MM-dd HH:mm:ss").format(date); |
|
||||
if (index == 0 && widget.postedAd.title != null) { |
|
||||
return Column(children: [ |
|
||||
// buildPostTile(context, widget.postedAd), |
|
||||
Divider(), |
|
||||
ListTile( |
|
||||
leading: Container( |
|
||||
width: 40, |
|
||||
height: 40, |
|
||||
decoration: BoxDecoration( |
|
||||
shape: BoxShape.circle, |
|
||||
border: Border.all( |
|
||||
color: Colors.black, |
|
||||
width: 1, |
|
||||
)), |
|
||||
child: ClipRRect( |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topLeft: Radius.circular(90.0), |
|
||||
topRight: Radius.circular(90.0), |
|
||||
bottomLeft: Radius.circular(90), |
|
||||
bottomRight: Radius.circular(90), |
|
||||
), |
|
||||
child: FadeInImage( |
|
||||
height: 90, |
|
||||
width: 90, |
|
||||
fit: BoxFit.fill, |
|
||||
image: NetworkImage(serverLocation + |
|
||||
"api/pulldp/" + |
|
||||
snapshot.data.docs[index] |
|
||||
.data()['commenterID']), |
|
||||
placeholder: AssetImage( |
|
||||
"assets/images/tesoDP/dp1.png"), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
title: new RichText( |
|
||||
text: new TextSpan( |
|
||||
style: new TextStyle( |
|
||||
fontSize: 14.0, |
|
||||
color: Theme.of(context).primaryColorLight, |
|
||||
), |
|
||||
children: <TextSpan>[ |
|
||||
new TextSpan( |
|
||||
text: snapshot.data.docs[index] |
|
||||
.data()['commenter'] + |
|
||||
" ", |
|
||||
style: new TextStyle( |
|
||||
fontWeight: FontWeight.bold)), |
|
||||
new TextSpan( |
|
||||
text: snapshot.data.docs[index] |
|
||||
.data()['comment']), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
subtitle: Text( |
|
||||
TimeElapsed.fromDateTime( |
|
||||
DateTime.parse(formattedDate)), |
|
||||
), |
|
||||
), |
|
||||
]); |
|
||||
} |
|
||||
return ListTile( |
|
||||
leading: Container( |
|
||||
width: 40, |
|
||||
height: 40, |
|
||||
decoration: BoxDecoration( |
|
||||
shape: BoxShape.circle, |
|
||||
border: Border.all( |
|
||||
color: Colors.black, |
|
||||
width: 1, |
|
||||
)), |
|
||||
child: ClipRRect( |
|
||||
borderRadius: BorderRadius.only( |
|
||||
topLeft: Radius.circular(90.0), |
|
||||
topRight: Radius.circular(90.0), |
|
||||
bottomLeft: Radius.circular(90), |
|
||||
bottomRight: Radius.circular(90), |
|
||||
), |
|
||||
child: FadeInImage( |
|
||||
height: 90, |
|
||||
width: 90, |
|
||||
fit: BoxFit.fill, |
|
||||
image: NetworkImage(serverLocation + |
|
||||
"api/pulldp/" + |
|
||||
snapshot.data.docs[index] |
|
||||
.data()['commenterID']), |
|
||||
placeholder: |
|
||||
AssetImage("assets/images/tesoDP/dp1.png"), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
title: new RichText( |
|
||||
text: new TextSpan( |
|
||||
style: new TextStyle( |
|
||||
fontSize: 14.0, |
|
||||
color: Theme.of(context).primaryColorLight, |
|
||||
), |
|
||||
children: <TextSpan>[ |
|
||||
new TextSpan( |
|
||||
text: snapshot.data.docs[index] |
|
||||
.data()['commenter'] + |
|
||||
" ", |
|
||||
style: new TextStyle( |
|
||||
fontWeight: FontWeight.bold)), |
|
||||
new TextSpan( |
|
||||
text: snapshot.data.docs[index] |
|
||||
.data()['comment']), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
subtitle: Text( |
|
||||
TimeElapsed.fromDateTime( |
|
||||
DateTime.parse(formattedDate)), |
|
||||
), |
|
||||
); |
|
||||
}, |
|
||||
// reverse: true, |
|
||||
controller: listScrollController, |
|
||||
); |
|
||||
} |
|
||||
}, |
|
||||
), |
|
||||
), |
|
||||
Align( |
|
||||
alignment: Alignment.bottomCenter, |
|
||||
child: buildBottom( |
|
||||
context, |
|
||||
controller, |
|
||||
sendComment, |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,204 +0,0 @@ |
|||||
import 'dart:convert'; |
|
||||
import 'dart:math'; |
|
||||
import 'package:http/http.dart' as http; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; |
|
||||
import 'package:flutter/cupertino.dart'; |
|
||||
import 'package:pull_to_refresh/pull_to_refresh.dart'; |
|
||||
import 'package:shared_preferences/shared_preferences.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/Post.dart'; |
|
||||
import 'package:teso/Pages/PageWidgets/Home/homeTile.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/homeSub/VideoList.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
|
|
||||
class HomeFeed extends StatefulWidget { |
|
||||
@override |
|
||||
_HomeFeedState createState() => _HomeFeedState(); |
|
||||
} |
|
||||
|
|
||||
class _HomeFeedState extends State<HomeFeed> { |
|
||||
ScrollController _controller; |
|
||||
// List<PostedAd> trends; |
|
||||
List<Post> show; |
|
||||
int count; |
|
||||
var _future; |
|
||||
List<Post> deadFeed; |
|
||||
RefreshController _refreshController = |
|
||||
RefreshController(initialRefresh: false); |
|
||||
|
|
||||
Future<List<Post>> pullAds() async { |
|
||||
try { |
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance(); |
|
||||
String token = prefs.getString("tokensTeso"); |
|
||||
Map<String, String> requestHeaders = { |
|
||||
'Content-type': 'application/json', |
|
||||
'Authorization': token |
|
||||
}; |
|
||||
var register = serverLocation + 'posts/homefeed'; |
|
||||
var client = |
|
||||
await http.post(Uri.parse(register), headers: requestHeaders); |
|
||||
if (client.statusCode == 200) { |
|
||||
var posts = jsonDecode(client.body); |
|
||||
setState(() { |
|
||||
this.show = List<Post>.from( |
|
||||
posts.map((model) => Post.fromJSON(model)).toList()); |
|
||||
// this.show.sort((b, a) => a.timestamp.compareTo(b.timestamp)); |
|
||||
}); |
|
||||
|
|
||||
await prefs.setString("homefeeds", client.body); |
|
||||
// await fetchImages(); |
|
||||
// |
|
||||
return show; |
|
||||
} else { |
|
||||
return null; |
|
||||
} |
|
||||
} catch (e) { |
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void _scrollListener() { |
|
||||
if (_controller.offset >= _controller.position.maxScrollExtent && |
|
||||
!_controller.position.outOfRange) { |
|
||||
pullAds(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void _onRefresh() async { |
|
||||
try { |
|
||||
await pullAds(); |
|
||||
} catch (e) { |
|
||||
print(e); |
|
||||
} |
|
||||
_refreshController.refreshCompleted(); |
|
||||
} |
|
||||
|
|
||||
Future<void> watch(advert) async { |
|
||||
List<Post> towatch = <Post>[]; |
|
||||
towatch.add(advert); |
|
||||
if (show.length != 0) { |
|
||||
for (int i = 0; (i < show.length) && (i < 10); i++) { |
|
||||
final _random = new Random(); |
|
||||
towatch.add(show[_random.nextInt(show.length)]); |
|
||||
} |
|
||||
} else if (show.length == 0) { |
|
||||
for (int i = 0; (i < deadFeed.length) && (i < 10); i++) { |
|
||||
final _random = new Random(); |
|
||||
towatch.add(deadFeed[_random.nextInt(show.length)]); |
|
||||
} |
|
||||
} |
|
||||
Post reported = await Navigator.of(context).push( |
|
||||
new PageRouteBuilder( |
|
||||
pageBuilder: (_, __, ___) => new VideoList( |
|
||||
postedAd: towatch, |
|
||||
// posts: towatch, |
|
||||
// user: user, |
|
||||
//friend: addable, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
if (reported != null) { |
|
||||
show.remove(reported); |
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance(); |
|
||||
await prefs.setString("homefeeds", jsonEncode(show)); |
|
||||
|
|
||||
print(prefs.getString("homefeed")); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void initState() { |
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) => widget.toggle(1)); |
|
||||
_controller = ScrollController(); |
|
||||
_controller.addListener(_scrollListener); |
|
||||
count = 0; |
|
||||
// pullAds(); |
|
||||
SharedPreferences.getInstance().then((value) { |
|
||||
if (value.getString("homefeeds") != null) { |
|
||||
var posts = jsonDecode(value.getString("homefeeds")); |
|
||||
setState(() { |
|
||||
deadFeed = List<Post>.from( |
|
||||
posts.map((model) => Post.fromJSON(model)).toList()); |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
_future = pullAds(); |
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
void dispose() { |
|
||||
_controller.dispose(); |
|
||||
_refreshController.dispose(); |
|
||||
super.dispose(); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Scaffold( |
|
||||
body: FutureBuilder( |
|
||||
initialData: deadFeed, |
|
||||
future: _future, |
|
||||
builder: (context, snapshot) { |
|
||||
if (show == null && |
|
||||
deadFeed == null && |
|
||||
(snapshot.connectionState == ConnectionState.none || |
|
||||
snapshot.connectionState == ConnectionState.waiting)) { |
|
||||
return Center( |
|
||||
child: CupertinoActivityIndicator( |
|
||||
animating: true, |
|
||||
radius: 15, |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
if (show == null && |
|
||||
deadFeed == null && |
|
||||
snapshot.connectionState == ConnectionState.done) { |
|
||||
return SmartRefresher( |
|
||||
enablePullDown: true, |
|
||||
enablePullUp: false, |
|
||||
header: ClassicHeader(), |
|
||||
controller: _refreshController, |
|
||||
onRefresh: _onRefresh, |
|
||||
child: Container( |
|
||||
child: Center( |
|
||||
child: Text("An error occurred"), |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} else { |
|
||||
show = show != null ? show : deadFeed; |
|
||||
return SmartRefresher( |
|
||||
enablePullDown: true, |
|
||||
enablePullUp: false, |
|
||||
header: ClassicHeader(), |
|
||||
controller: _refreshController, |
|
||||
onRefresh: _onRefresh, |
|
||||
child: StaggeredGridView.count( |
|
||||
controller: _controller, |
|
||||
crossAxisCount: 2, |
|
||||
children: List.generate(show.length, (int index) { |
|
||||
return show.elementAt(index).aspect == null || |
|
||||
show.elementAt(index).aspect == "null" |
|
||||
? Container() |
|
||||
: double.parse(show.elementAt(index).aspect.toString()) < |
|
||||
1 |
|
||||
? buildTile( |
|
||||
context, show.elementAt(index), 0.65, watch) |
|
||||
: buildTile( |
|
||||
context, show.elementAt(index), 0.35, watch); |
|
||||
}), |
|
||||
staggeredTiles: List.generate( |
|
||||
show.length, |
|
||||
(int index) { |
|
||||
return StaggeredTile.fit(1); |
|
||||
}, |
|
||||
), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
}, |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1,66 +0,0 @@ |
|||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter/rendering.dart'; |
|
||||
|
|
||||
import 'package:provider/provider.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/Post.dart'; |
|
||||
import 'package:teso/Pages/Sub_Pages/Posts/ViewPost.dart'; |
|
||||
import 'package:teso/providers/user_provider.dart'; |
|
||||
|
|
||||
class VideoList extends StatefulWidget { |
|
||||
final List<Post> postedAd; |
|
||||
|
|
||||
VideoList({Key key, this.postedAd}) : super(key: key); |
|
||||
|
|
||||
@override |
|
||||
_VideoListState createState() => _VideoListState(); |
|
||||
} |
|
||||
|
|
||||
class _VideoListState extends State<VideoList> { |
|
||||
@override |
|
||||
void initState() { |
|
||||
Provider.of<UserProvider>(context, listen: false) |
|
||||
.predownloadAds(widget.postedAd.map((e) => e.playbackID).toList()); |
|
||||
super.initState(); |
|
||||
} |
|
||||
|
|
||||
report(Post post) { |
|
||||
Navigator.pop(context, post); |
|
||||
Navigator.pop(context, post); |
|
||||
} |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return Scaffold( |
|
||||
appBar: AppBar( |
|
||||
automaticallyImplyLeading: true, |
|
||||
backgroundColor: Colors.transparent, |
|
||||
leading: InkWell( |
|
||||
onTap: () => Navigator.pop(context), |
|
||||
child: Container( |
|
||||
height: 50, |
|
||||
width: 40, |
|
||||
margin: EdgeInsets.symmetric( |
|
||||
vertical: 30.0, |
|
||||
horizontal: 10, |
|
||||
), |
|
||||
child: Icon( |
|
||||
Icons.arrow_back_ios, |
|
||||
color: Colors.white, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
extendBodyBehindAppBar: true, |
|
||||
body: PageView( |
|
||||
scrollDirection: Axis.vertical, |
|
||||
children: List<ViewPost>.generate( |
|
||||
widget.postedAd.length > 10 ? 10 : widget.postedAd.length, |
|
||||
(index) => new ViewPost( |
|
||||
play: true, |
|
||||
postedAd: widget.postedAd[index], |
|
||||
report: report, |
|
||||
)), |
|
||||
), |
|
||||
); |
|
||||
} |
|
||||
} |
|
@ -1 +0,0 @@ |
|||||
export 'uservideo_controller_service.dart'; |
|
@ -1 +0,0 @@ |
|||||
export 'video_controller_service.dart'; |
|
@ -1,67 +0,0 @@ |
|||||
import 'package:better_player/better_player.dart'; |
|
||||
import 'package:cached_network_image/cached_network_image.dart'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart'; |
|
||||
import 'package:teso/Classes/Firebase/Posts.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
|
|
||||
abstract class VideoControllerService { |
|
||||
Future<BetterPlayerController> getControllerForVideo(FBPosts video); |
|
||||
} |
|
||||
|
|
||||
class CachedVideoControllerService extends VideoControllerService { |
|
||||
// ignore: unused_field |
|
||||
final BaseCacheManager _cacheManager; |
|
||||
|
|
||||
CachedVideoControllerService(this._cacheManager) |
|
||||
: assert(_cacheManager != null); |
|
||||
|
|
||||
@override |
|
||||
Future<BetterPlayerController> getControllerForVideo(FBPosts video) async { |
|
||||
try { |
|
||||
BetterPlayerDataSource betterPlayerDataSource = BetterPlayerDataSource( |
|
||||
BetterPlayerDataSourceType.network, |
|
||||
"https://stream.mux.com/${video.playbackID}.m3u8", |
|
||||
videoFormat: BetterPlayerVideoFormat.hls, |
|
||||
cacheConfiguration: BetterPlayerCacheConfiguration( |
|
||||
useCache: true, |
|
||||
), |
|
||||
); |
|
||||
|
|
||||
return BetterPlayerController( |
|
||||
BetterPlayerConfiguration( |
|
||||
autoPlay: true, |
|
||||
aspectRatio: double.tryParse(video.aspect), |
|
||||
looping: true, |
|
||||
fit: double.parse(video.aspect) < 1 |
|
||||
? BoxFit.fitHeight |
|
||||
: BoxFit.fitWidth, |
|
||||
showPlaceholderUntilPlay: true, |
|
||||
placeholder: Container( |
|
||||
child: Center( |
|
||||
child: CachedNetworkImage( |
|
||||
imageUrl: tesoPostThumb(video.playbackID), |
|
||||
imageBuilder: (context, imageProvider) => FadeInImage( |
|
||||
width: double.infinity, |
|
||||
fit: BoxFit.fill, |
|
||||
image: imageProvider, |
|
||||
placeholder: AssetImage("assets/images/blank.jpg"), |
|
||||
), |
|
||||
errorWidget: (context, url, error) => |
|
||||
Image.asset("assets/images/blank.jpg"), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
controlsConfiguration: BetterPlayerControlsConfiguration( |
|
||||
showControls: false, |
|
||||
), |
|
||||
autoDispose: true, |
|
||||
), |
|
||||
betterPlayerDataSource: betterPlayerDataSource, |
|
||||
); |
|
||||
} catch (e) { |
|
||||
// return BetterPlayerController.network(tesoStreaming + "pd/" + video.path); |
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,66 +0,0 @@ |
|||||
import 'package:better_player/better_player.dart'; |
|
||||
import 'package:cached_network_image/cached_network_image.dart'; |
|
||||
import 'package:flutter/cupertino.dart'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart'; |
|
||||
import 'package:teso/Classes/API%20Clasess/Post.dart'; |
|
||||
import 'package:teso/util/consts.dart'; |
|
||||
|
|
||||
abstract class VideoControllerService { |
|
||||
Future<BetterPlayerController> getControllerForVideo(Post video); |
|
||||
} |
|
||||
|
|
||||
class CachedVideoControllerService extends VideoControllerService { |
|
||||
// ignore: unused_field |
|
||||
final BaseCacheManager _cacheManager; |
|
||||
|
|
||||
CachedVideoControllerService(this._cacheManager) |
|
||||
: assert(_cacheManager != null); |
|
||||
|
|
||||
@override |
|
||||
Future<BetterPlayerController> getControllerForVideo(Post video) async { |
|
||||
try { |
|
||||
BetterPlayerDataSource betterPlayerDataSource = BetterPlayerDataSource( |
|
||||
BetterPlayerDataSourceType.network, |
|
||||
"https://stream.mux.com/${video.playbackID}.m3u8", |
|
||||
videoFormat: BetterPlayerVideoFormat.hls, |
|
||||
cacheConfiguration: BetterPlayerCacheConfiguration( |
|
||||
useCache: true, |
|
||||
), |
|
||||
); |
|
||||
|
|
||||
return BetterPlayerController( |
|
||||
BetterPlayerConfiguration( |
|
||||
autoPlay: true, |
|
||||
aspectRatio: double.tryParse(video.aspect), |
|
||||
looping: true, |
|
||||
fit: BoxFit.fill, |
|
||||
showPlaceholderUntilPlay: true, |
|
||||
placeholder: Container( |
|
||||
child: Center( |
|
||||
child: CachedNetworkImage( |
|
||||
imageUrl: tesoPostThumb(video.playbackID), |
|
||||
imageBuilder: (context, imageProvider) => FadeInImage( |
|
||||
width: double.infinity, |
|
||||
fit: BoxFit.fill, |
|
||||
image: imageProvider, |
|
||||
placeholder: AssetImage("assets/images/blank.jpg"), |
|
||||
), |
|
||||
errorWidget: (context, url, error) => |
|
||||
Image.asset("assets/images/blank.jpg"), |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
controlsConfiguration: BetterPlayerControlsConfiguration( |
|
||||
showControls: false, |
|
||||
), |
|
||||
autoDispose: true, |
|
||||
), |
|
||||
betterPlayerDataSource: betterPlayerDataSource, |
|
||||
); |
|
||||
} catch (e) { |
|
||||
// return BetterPlayerController.network(tesoStreaming + "pd/" + video.path); |
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1 +0,0 @@ |
|||||
export 'video_player/video_player.dart'; |
|
@ -1,3 +0,0 @@ |
|||||
export 'video_player_bloc.dart'; |
|
||||
export 'uservideo_player_event.dart'; |
|
||||
export 'uservideo_player_state.dart'; |
|
@ -1,28 +0,0 @@ |
|||||
import 'package:bloc/bloc.dart'; |
|
||||
import 'package:teso/Services/services.dart'; |
|
||||
|
|
||||
import 'uservideo_player.dart'; |
|
||||
|
|
||||
class VideoPlayerBloc extends Bloc<VideoPlayerEvent, VideoPlayerState> { |
|
||||
final VideoControllerService _videoControllerService; |
|
||||
|
|
||||
VideoPlayerBloc(this._videoControllerService) |
|
||||
: assert(_videoControllerService != null), |
|
||||
super(null); |
|
||||
|
|
||||
VideoPlayerState get initialState => VideoPlayerStateInitial(); |
|
||||
|
|
||||
@override |
|
||||
Stream<VideoPlayerState> mapEventToState(VideoPlayerEvent event) async* { |
|
||||
if (event is VideoSelectedEvent) { |
|
||||
yield VideoPlayerStateLoading(); |
|
||||
try { |
|
||||
final videoController = |
|
||||
await _videoControllerService.getControllerForVideo(event.video); |
|
||||
yield VideoPlayerStateLoaded(event.video, videoController); |
|
||||
} catch (e) { |
|
||||
yield VideoPlayerStateError(e.message ?? 'An unknown error occurred'); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
@ -1,16 +0,0 @@ |
|||||
import 'package:equatable/equatable.dart'; |
|
||||
import 'package:teso/Classes/Firebase/Posts.dart'; |
|
||||
|
|
||||
abstract class VideoPlayerEvent extends Equatable { |
|
||||
@override |
|
||||
List<Object> get props => const []; |
|
||||
} |
|
||||
|
|
||||
class VideoSelectedEvent extends VideoPlayerEvent { |
|
||||
final FBPosts video; |
|
||||
|
|
||||
VideoSelectedEvent(this.video) : assert(video != null); |
|
||||
|
|
||||
@override |
|
||||
List<Object> get props => [video]; |
|
||||
} |
|
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue