{"id":22495589,"url":"https://github.com/brutalcoding/video_trimmer","last_synced_at":"2025-07-09T13:35:29.563Z","repository":{"id":71946321,"uuid":"299010708","full_name":"BrutalCoding/video_trimmer","owner":"BrutalCoding","description":null,"archived":false,"fork":false,"pushed_at":"2020-09-27T23:12:40.000Z","size":2043,"stargazers_count":0,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-28T16:04:47.546Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Dart","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"litt-global/video_trimmer","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BrutalCoding.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":null,"patreon":"souvikbiswas","open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":"sbis04","otechie":null,"custom":null}},"created_at":"2020-09-27T10:50:02.000Z","updated_at":"2020-09-27T23:12:43.000Z","dependencies_parsed_at":"2024-11-25T04:09:59.232Z","dependency_job_id":null,"html_url":"https://github.com/BrutalCoding/video_trimmer","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BrutalCoding%2Fvideo_trimmer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BrutalCoding%2Fvideo_trimmer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BrutalCoding%2Fvideo_trimmer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BrutalCoding%2Fvideo_trimmer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BrutalCoding","download_url":"https://codeload.github.com/BrutalCoding/video_trimmer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247238585,"owners_count":20906467,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-06T19:44:56.517Z","updated_at":"2025-04-04T19:36:56.194Z","avatar_url":"https://github.com/BrutalCoding.png","language":"Dart","funding_links":["https://patreon.com/souvikbiswas","https://issuehunt.io/r/sbis04"],"categories":[],"sub_categories":[],"readme":"\u003ca href=\"https://github.com/Solido/awesome-flutter\"\u003e\n   \u003cimg alt=\"Awesome Flutter\" src=\"https://img.shields.io/badge/Awesome-Flutter-blue.svg?longCache=true\u0026style=flat-square\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://pub.dev/packages/video_trimmer\"\u003e\n  \u003cimg alt=\"Pub Version\" src=\"https://img.shields.io/pub/v/video_trimmer?style=flat-square\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/sbis04/video_trimmer/stargazers\"\u003e\n  \u003cimg alt=\"GitHub stars\" src=\"https://img.shields.io/github/stars/sbis04/video_trimmer?style=flat-square\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/sbis04/video_trimmer/blob/master/LICENSE\"\u003e\n  \u003cimg alt=\"GitHub license\" src=\"https://img.shields.io/github/license/sbis04/video_trimmer?style=flat-square\"\u003e\n\u003c/a\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/sbis04/video_trimmer/raw/master/screenshots/cover.png\" alt=\"Video Trimmer\" /\u003e\n\u003c/p\u003e\n\n\u003ch4 align=\"center\"\u003eA Flutter package for trimming videos\u003c/h4\u003e\n\n### Features\n\n* Customizable video trimmer\n* Video playback control\n* Retrieving and storing video file\n\nAlso, supports conversion to **GIF**.\n\n\u003ch4 align=\"center\"\u003eTRIM EDITOR\u003c/h4\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/sbis04/video_trimmer/raw/master/screenshots/editor_demo.gif\" alt=\"Trim Editor\" /\u003e\n\u003c/p\u003e\n\n\u003ch4 align=\"center\"\u003eEXAMPLE APP\u003c/h4\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/sbis04/video_trimmer/raw/master/screenshots/trimmer.png\" alt=\"Trimmer\"/\u003e\n\u003c/p\u003e\n\n\u003ch4 align=\"center\"\u003eCUSTOMIZABLE VIDEO EDITOR\u003c/h4\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/sbis04/video_trimmer/raw/master/screenshots/trim_editor.gif\" alt=\"Trim Editor\" /\u003e\n\u003c/p\u003e\n\n## Usage\n\n* Add the dependency `video_trimmer` to your **pubspec.yaml** file.\n\n### Android\n\n* Go to `\u003cproject root\u003e/android/app/build.gradle` and set the proper `minSdkVersion`, **24** for **Main Release** or **16** for **LTS Release**. \n  \n  \u003e Refer to the [FFmpeg Release](#ffmpeg-release) section.\n\n   ```gradle\n   minSdkVersion \u003cversion\u003e\n   ```\n* Go to `\u003cproject root\u003e/android/build.gradle` and add the following line:\n\n   ```gradle\n   ext.flutterFFmpegPackage = '\u003cpackage name\u003e'\n   ```\n\n   \u003e Replace the `\u003cpackage name\u003e` with a proper package name from the [Packages List](#packages-list) section.\n\n### iOS\n\n* Add the following keys to your **Info.plist** file, located in `\u003cproject root\u003e/ios/Runner/Info.plist`:\n  ```\n  \u003ckey\u003eNSCameraUsageDescription\u003c/key\u003e\n  \u003cstring\u003eUsed to demonstrate image picker plugin\u003c/string\u003e\n  \u003ckey\u003eNSMicrophoneUsageDescription\u003c/key\u003e\n  \u003cstring\u003eUsed to capture audio for image picker plugin\u003c/string\u003e\n  \u003ckey\u003eNSPhotoLibraryUsageDescription\u003c/key\u003e\n  \u003cstring\u003eUsed to demonstrate image picker plugin\u003c/string\u003e\n  ```\n\n* Set the platform version in `ios/Podfile`, **12.1** for **Main Release** or **9.3** for **LTS Release**.\n  \n  \u003e Refer to the [FFmpeg Release](#ffmpeg-release) section.\n\n   ```\n   platform :ios, '\u003cversion\u003e'\n   ```\n\n* Replace with the following in the `# Plugin Pods` section of the `ios/Podfile`: \n\n   ```\n   # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock\n   # referring to absolute paths on developers' machines.\n\n   system('rm -rf .symlinks')\n   system('mkdir -p .symlinks/plugins')\n   plugin_pods = parse_KV_file('../.flutter-plugins')\n   plugin_pods.each do |name, path|\n     symlink = File.join('.symlinks', 'plugins', name)\n     File.symlink(path, symlink)\n     if name == 'flutter_ffmpeg'\n         pod name+'/\u003cpackage name\u003e', :path =\u003e File.join(symlink, 'ios')\n     else\n         pod name, :path =\u003e File.join(symlink, 'ios')\n     end\n   end\n   ```\n\n   \u003e Replace the `\u003cpackage name\u003e` with a proper package name from the [Packages List](#packages-list) section.\n\n\n### FFmpeg Release\n\nIn reference to the releases specified in the [flutter_ffmpeg](https://pub.dev/packages/flutter_ffmpeg) package.\n\n\u003ctable\u003e\n\u003cthead\u003e\n    \u003ctr\u003e\n        \u003cth align=\"center\"\u003e\u003c/th\u003e\n        \u003cth align=\"center\"\u003eMain Release\u003c/th\u003e\n        \u003cth align=\"center\"\u003eLTS Release\u003c/th\u003e\n    \u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n    \u003ctr\u003e\n        \u003ctd align=\"center\"\u003eAndroid API Level\u003c/td\u003e\n        \u003ctd align=\"center\"\u003e24\u003c/td\u003e\n        \u003ctd align=\"center\"\u003e16\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd align=\"center\"\u003eAndroid Camera Access\u003c/td\u003e\n        \u003ctd align=\"center\"\u003eYes\u003c/td\u003e\n        \u003ctd align=\"center\"\u003e-\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd align=\"center\"\u003eAndroid Architectures\u003c/td\u003e\n        \u003ctd align=\"center\"\u003earm-v7a-neon\u003cbr\u003earm64-v8a\u003cbr\u003ex86\u003cbr\u003ex86-64\u003c/td\u003e\n        \u003ctd align=\"center\"\u003earm-v7a\u003cbr\u003earm-v7a-neon\u003cbr\u003earm64-v8a\u003cbr\u003ex86\u003cbr\u003ex86-64\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd align=\"center\"\u003eXcode Support\u003c/td\u003e\n        \u003ctd align=\"center\"\u003e10.1\u003c/td\u003e\n        \u003ctd align=\"center\"\u003e7.3.1\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd align=\"center\"\u003eiOS SDK\u003c/td\u003e\n        \u003ctd align=\"center\"\u003e12.1\u003c/td\u003e\n        \u003ctd align=\"center\"\u003e9.3\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n        \u003ctd align=\"center\"\u003eiOS Architectures\u003c/td\u003e\n        \u003ctd align=\"center\"\u003earm64\u003cbr\u003earm64e\u003cbr\u003ex86-64\u003c/td\u003e\n        \u003ctd align=\"center\"\u003earmv7\u003cbr\u003earm64\u003cbr\u003ei386\u003cbr\u003ex86-64\u003c/td\u003e\n    \u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n### Packages List\n\nThe following **FFmpeg Packages** List is in reference to the [flutter_ffmpeg](https://pub.dev/packages/flutter_ffmpeg) package.\n\n| Package | Main Release | LTS Release |\n| :----: | :----: | :----: |\n| min | min  | min-lts |\n| min-gpl | min-gpl | min-gpl-lts |\n| https | https | https-lts |\n| https-gpl | https-gpl | https-gpl-lts |\n| audio | audio | audio-lts |\n| video | video | video-lts |\n| full | full | full-lts |\n| full-gpl | full-gpl | full-gpl-lts |\n\n## Functionalities\n\n### Loading input video file\n\n```dart\nfinal Trimmer _trimmer = Trimmer();\nawait _trimmer.loadVideo(videoFile: file);\n```\n\n### Saving trimmed video\n\nReturns a string to indicate whether the saving operation was successful.\n\n```dart\nawait _trimmer\n    .saveTrimmedVideo(startValue: _startValue, endValue: _endValue)\n    .then((value) {\n  setState(() {\n    _value = value;\n  });\n});\n```\n\n### Video playback state \n\nReturns the video playback state. If **true** then the video is playing, otherwise it is paused.\n\n```dart\nawait _trimmer.videPlaybackControl(\n  startValue: _startValue,\n  endValue: _endValue,\n);\n```\n\n### Advanced Command\n\nYou can use an advanced **FFmpeg** command if you require more customization. Just define your FFmpeg command using the `ffmpegCommand` property and set an output video format using `customVideoFormat`. \n\nRefer to the [Official FFmpeg Documentation](https://ffmpeg.org/documentation.html) for more information.\n\n\u003e **NOTE:** Passing a wrong video format to the `customVideoFormat` property may result in a crash.\n\n```dart\n// Example of defining a custom command\n\n// This is already used for creating GIF by\n// default, so you do not need to use this.\n\nawait _trimmer\n    .saveTrimmedVideo(\n        startValue: _startValue,\n        endValue: _endValue,\n        ffmpegCommand:\n            '-vf \"fps=10,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse\" -loop 0',\n        customVideoFormat: '.gif')\n    .then((value) {\n  setState(() {\n    _value = value;\n  });\n});\n```\n\n## Widgets\n\n### Display a video playback area\n\n```dart\nVideoViewer()\n```\n\n### Display the video trimmer area\n\n```dart\nTrimEditor(\n  viewerHeight: 50.0,\n  viewerWidth: MediaQuery.of(context).size.width,\n  onChangeStart: (value) {\n    _startValue = value;\n  },\n  onChangeEnd: (value) {\n    _endValue = value;\n  },\n  onChangePlaybackState: (value) {\n    setState(() {\n      _isPlaying = value;\n    });\n  },\n)\n```\n\n## Example\n\n```dart\nimport 'dart:io';\n\nimport 'package:flutter/material.dart';\nimport 'package:image_picker/image_picker.dart';\nimport 'package:video_trimmer/trim_editor.dart';\nimport 'package:video_trimmer/video_trimmer.dart';\nimport 'package:video_trimmer/video_viewer.dart';\n\nvoid main() =\u003e runApp(MyApp());\n\nclass MyApp extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      title: 'Video Trimmer',\n      theme: ThemeData(\n        primarySwatch: Colors.blue,\n      ),\n      home: HomePage(),\n    );\n  }\n}\n\nclass HomePage extends StatelessWidget {\n  final Trimmer _trimmer = Trimmer();\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(\"Video Trimmer\"),\n      ),\n      body: Center(\n        child: Container(\n          child: RaisedButton(\n            child: Text(\"LOAD VIDEO\"),\n            onPressed: () async {\n              File file = await ImagePicker.pickVideo(\n                source: ImageSource.gallery,\n              );\n              if (file != null) {\n                await _trimmer.loadVideo(videoFile: file);\n                Navigator.of(context)\n                    .push(MaterialPageRoute(builder: (context) {\n                  return TrimmerView(_trimmer);\n                }));\n              }\n            },\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nclass TrimmerView extends StatefulWidget {\n  final Trimmer _trimmer;\n  TrimmerView(this._trimmer);\n  @override\n  _TrimmerViewState createState() =\u003e _TrimmerViewState();\n}\n\nclass _TrimmerViewState extends State\u003cTrimmerView\u003e {\n  double _startValue = 0.0;\n  double _endValue = 0.0;\n\n  bool _isPlaying = false;\n  bool _progressVisibility = false;\n\n  Future\u003cString\u003e _saveVideo() async {\n    setState(() {\n      _progressVisibility = true;\n    });\n\n    String _value;\n\n    await widget._trimmer\n        .saveTrimmedVideo(startValue: _startValue, endValue: _endValue)\n        .then((value) {\n      setState(() {\n        _progressVisibility = false;\n        _value = value;\n      });\n    });\n\n    return _value;\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text(\"Video Trimmer\"),\n      ),\n      body: Builder(\n        builder: (context) =\u003e Center(\n          child: Container(\n            padding: EdgeInsets.only(bottom: 30.0),\n            color: Colors.black,\n            child: Column(\n              mainAxisAlignment: MainAxisAlignment.center,\n              mainAxisSize: MainAxisSize.max,\n              children: \u003cWidget\u003e[\n                Visibility(\n                  visible: _progressVisibility,\n                  child: LinearProgressIndicator(\n                    backgroundColor: Colors.red,\n                  ),\n                ),\n                RaisedButton(\n                  onPressed: _progressVisibility\n                      ? null\n                      : () async {\n                          _saveVideo().then((outputPath) {\n                            print('OUTPUT PATH: $outputPath');\n                            final snackBar = SnackBar(content: Text('Video Saved successfully'));\n                            Scaffold.of(context).showSnackBar(snackBar);\n                          });\n                        },\n                  child: Text(\"SAVE\"),\n                ),\n                Expanded(\n                  child: VideoViewer(),\n                ),\n                Center(\n                  child: TrimEditor(\n                    viewerHeight: 50.0,\n                    viewerWidth: MediaQuery.of(context).size.width,\n                    onChangeStart: (value) {\n                      _startValue = value;\n                    },\n                    onChangeEnd: (value) {\n                      _endValue = value;\n                    },\n                    onChangePlaybackState: (value) {\n                      setState(() {\n                        _isPlaying = value;\n                      });\n                    },\n                  ),\n                ),\n                FlatButton(\n                  child: _isPlaying\n                      ? Icon(\n                          Icons.pause,\n                          size: 80.0,\n                          color: Colors.white,\n                        )\n                      : Icon(\n                          Icons.play_arrow,\n                          size: 80.0,\n                          color: Colors.white,\n                        ),\n                  onPressed: () async {\n                    bool playbackState =\n                        await widget._trimmer.videPlaybackControl(\n                      startValue: _startValue,\n                      endValue: _endValue,\n                    );\n                    setState(() {\n                      _isPlaying = playbackState;\n                    });\n                  },\n                )\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n```\n\n## License\n\nCopyright (c) 2020 Souvik Biswas\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrutalcoding%2Fvideo_trimmer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrutalcoding%2Fvideo_trimmer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrutalcoding%2Fvideo_trimmer/lists"}