├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── bin └── flutter_create.dart ├── example └── README.md ├── flutter_create.sh ├── lib ├── flutter_create.dart └── src │ ├── file_repository_extension.dart │ └── flutter_create_base.dart ├── pubspec.yaml └── test └── flutter_create_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub 2 | # vscode settings 3 | .vscode 4 | 5 | .dart_tool/ 6 | .packages 7 | # Remove the following pattern if you wish to check in your lock file 8 | pubspec.lock 9 | 10 | # Conventional directory for build outputs 11 | build/ 12 | 13 | 14 | # Directory created by dartdoc 15 | doc/api/ 16 | 17 | 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.4 2 | 3 | - Null safety 4 | 5 | 6 | ## 1.0.3 7 | 8 | - fixed error -> platform specific folder not available 9 | 10 | 11 | ## 1.0.2 12 | 13 | - Clarify description II 14 | 15 | ## 1.0.1 16 | 17 | - Clarify description 18 | 19 | ## 1.0.0 20 | 21 | - Initial version, created by Stagehand 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Odun Adeboye 2 | Permission is hereby granted, free of charge, to any person obtaining a copy 3 | of this software and associated documentation files (the "Software"), to deal 4 | in the Software without restriction, including without limitation the rights 5 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 6 | copies of the Software, and to permit persons to whom the Software is 7 | furnished to do so, subject to the following conditions: 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 13 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 14 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 15 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 16 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | For creating custom flutter project from template 2 | A standalone solution to issue https://github.com/flutter/flutter/issues/15279 3 | 4 | ## Installation 5 | 6 | If you want to use flutter_create on the command line, 7 | install it using `pub global activate`: 8 | 9 | ```console 10 | > pub global activate flutter_create 11 | ``` 12 | 13 | To update flutter_create, use the same `pub global activate` command. 14 | 15 | ## Usage 16 | 17 | ```console 18 | > flutter_create -a appname -u template_url 19 | ``` 20 | or 21 | 22 | ```console 23 | > pub global run flutter_create -a appname -u template_url 24 | ``` 25 | 26 | ```console 27 | 28 | > cd appname 29 | > flutter pub get 30 | > flutter run 31 | 32 | ``` 33 | 34 | ## Features and bugs 35 | 36 | Please file feature requests and bugs at the [issue tracker][tracker]. 37 | 38 | [tracker]: https://github.com/odunboye/flutter_create/issues 39 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:lints/recommended.yaml 2 | -------------------------------------------------------------------------------- /bin/flutter_create.dart: -------------------------------------------------------------------------------- 1 | import 'package:args/args.dart'; 2 | import 'package:flutter_create/flutter_create.dart'; 3 | 4 | const android = 'android'; 5 | const macOS = 'macOS'; 6 | const ios = 'ios'; 7 | 8 | const target = 'target'; 9 | const appname = 'appname'; 10 | const url = 'url'; 11 | const bundleId = 'bundleId'; 12 | const launcherIcon = 'launcherIcon'; 13 | const help = 'help'; 14 | 15 | final argParser = ArgParser() 16 | ..addOption(appname, abbr: 'a', help: 'Sets the name of the app.') 17 | ..addOption(url, abbr: 'u', help: 'Sets the template repository url.') 18 | ..addFlag(help, abbr: 'h', help: 'Shows help.', negatable: false); 19 | 20 | void main(List arguments) async { 21 | try { 22 | final results = argParser.parse(arguments); 23 | if (results[help] || results.arguments.isEmpty) { 24 | print(argParser.usage); 25 | return; 26 | } 27 | 28 | if (results[appname] != null) { 29 | var sourceCreator = FlutterCreate(); 30 | sourceCreator.create( 31 | args: [ 32 | results[url], //url 33 | results[appname], //'my_donation' 34 | ], 35 | appName: results[appname], 36 | ); 37 | } 38 | } on FormatException catch (e) { 39 | print(e.message); 40 | print(''); 41 | print(argParser.usage); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | Usage 2 | 3 | flutter_create -a my_awesome_app -u https://github.com/VictorUvarov/provider_architecture_template -------------------------------------------------------------------------------- /flutter_create.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # flutter_create.sh 3 | # echo "You provided $# arguments" 4 | echo "Creating new flutter project - $1" 5 | echo "using custom template - $2 ..." 6 | 7 | if (($# == 2)) 8 | then 9 | NAME=$1 10 | URL=$2 11 | git clone $URL $NAME 12 | cd $NAME 13 | rename -t android -t ios --appname $NAME 14 | else 15 | echo "Usage: flutter_create.sh [appname] [template url]" 16 | fi -------------------------------------------------------------------------------- /lib/flutter_create.dart: -------------------------------------------------------------------------------- 1 | /// Support for doing something awesome. 2 | /// 3 | /// More dartdocs go here. 4 | library flutter_create; 5 | 6 | export 'src/flutter_create_base.dart'; 7 | 8 | // TODO: Export any libraries intended for clients of this package. 9 | -------------------------------------------------------------------------------- /lib/src/file_repository_extension.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:rename/file_repository.dart'; 4 | import 'package:path/path.dart' as p; 5 | 6 | extension MyFileRepositry on FileRepository { 7 | static String pubspecPath = 'pubspec.yaml'; 8 | 9 | void hello() { 10 | print('hello from extension'); 11 | } 12 | 13 | Future myChangeIosAppName(String appName) async { 14 | try { 15 | List? contentLineByLine = await readFileAsLineByline( 16 | filePath: p.join( 17 | appName, 18 | iosInfoPlistPath, 19 | )); 20 | 21 | for (var i = 0; i < contentLineByLine!.length; i++) { 22 | if (contentLineByLine[i]!.contains('CFBundleName')) { 23 | contentLineByLine[i + 1] = '\t${appName}\r'; 24 | break; 25 | } 26 | } 27 | await writeFile( 28 | filePath: p.join( 29 | appName, 30 | iosInfoPlistPath, 31 | ), 32 | content: contentLineByLine.join('\n'), 33 | ); 34 | print('IOS appname changed successfully to : $appName'); 35 | } catch (e) { 36 | print('Error changing iOS appname - iOS folder possibly not there'); 37 | } 38 | } 39 | 40 | Future myChangeMacOsAppName(String appName) async { 41 | try { 42 | List? contentLineByLine = await readFileAsLineByline( 43 | filePath: p.join( 44 | appName, 45 | macosAppInfoxprojPath, 46 | )); 47 | for (var i = 0; i < contentLineByLine!.length; i++) { 48 | if (contentLineByLine[i]!.contains('PRODUCT_NAME')) { 49 | contentLineByLine[i] = 'PRODUCT_NAME = $appName;'; 50 | break; 51 | } 52 | } 53 | await writeFile( 54 | filePath: p.join( 55 | appName, 56 | macosAppInfoxprojPath, 57 | ), 58 | content: contentLineByLine.join('\n'), 59 | ); 60 | print('MacOS appname changed successfully to : $appName'); 61 | } catch (e) { 62 | print('Error changing macOS appname - MacOS folder possibly not there'); 63 | } 64 | } 65 | 66 | Future myChangeAndroidAppName(String appName) async { 67 | try { 68 | List? contentLineByLine = await readFileAsLineByline( 69 | filePath: p.join( 70 | appName, 71 | androidManifestPath, 72 | ), 73 | ); 74 | for (var i = 0; i < contentLineByLine!.length; i++) { 75 | if (contentLineByLine[i]!.contains('android:label')) { 76 | contentLineByLine[i] = ' android:label=\"${appName}\"'; 77 | break; 78 | } 79 | } 80 | await writeFile( 81 | filePath: p.join( 82 | appName, 83 | androidManifestPath, 84 | ), 85 | content: contentLineByLine.join('\n'), 86 | ); 87 | print('Android appname changed successfully to : $appName'); 88 | } catch (e) { 89 | print( 90 | 'Error changing Android appname - Anroid folder possibly not there'); 91 | } 92 | } 93 | 94 | Future changeImportName( 95 | String appName, String filePath, String oldAppName) async { 96 | List? contentLineByLine = await readFileAsLineByline( 97 | filePath: filePath, 98 | ); 99 | var oldApp = oldAppName.trim(); 100 | var newApp = appName.trim(); 101 | for (var i = 0; i < contentLineByLine!.length; i++) { 102 | if (contentLineByLine[i]!.indexOf(oldApp) > 0) { 103 | contentLineByLine[i] = 104 | contentLineByLine[i]!.replaceFirst(oldApp, '$newApp'); 105 | } 106 | } 107 | var writtenFile = await writeFile( 108 | filePath: filePath, 109 | content: contentLineByLine.join('\n'), 110 | ); 111 | print('Import $oldApp changed successfully to : $newApp'); 112 | return writtenFile; 113 | } 114 | 115 | Future getCurrentPubSpecName(String dirPath) async { 116 | String? ret; 117 | List? contentLineByLine = await readFileAsLineByline( 118 | filePath: p.join(dirPath, pubspecPath), 119 | ); 120 | 121 | for (var i = 0; i < contentLineByLine!.length; i++) { 122 | if (contentLineByLine[i]!.contains('name:')) { 123 | ret = (contentLineByLine[i] as String).split(':')[1]; 124 | } 125 | } 126 | return ret; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /lib/src/flutter_create_base.dart: -------------------------------------------------------------------------------- 1 | import 'package:path/path.dart' as p; 2 | import 'package:rename/file_repository.dart'; 3 | import 'file_repository_extension.dart'; 4 | import 'dart:io'; 5 | 6 | enum Platform { 7 | android, 8 | ios, 9 | macOS, 10 | } 11 | 12 | class FlutterCreate { 13 | bool get isAwesome => true; 14 | FileRepository fileRepository = FileRepository(); 15 | 16 | void create({required List args, required String appName}) async { 17 | args.insert(0, 'clone'); 18 | await Process.run('git', args, 19 | workingDirectory: p.current, runInShell: true) 20 | .then((result) { 21 | stdout.write(result.stdout); 22 | stderr.write(result.stderr); 23 | }); 24 | await changeAppName( 25 | appName: appName, platforms: [Platform.android, Platform.ios]); 26 | } 27 | 28 | void changeFilesImports(String appName, String oldAppName) { 29 | var dir = Directory(appName); 30 | // List directory contents, recursing into sub-directories, 31 | // but not following symbolic links. 32 | dir 33 | .list(recursive: true, followLinks: false) 34 | .listen((FileSystemEntity entity) { 35 | if (entity.path.endsWith('dart') || entity.path.endsWith('.yaml')) { 36 | fileRepository.changeImportName(appName, entity.path, oldAppName); 37 | } 38 | }); 39 | } 40 | 41 | Future getPubSpecName(String path) async { 42 | return fileRepository.getCurrentPubSpecName(path); 43 | } 44 | 45 | // change rename.changeAppName 46 | Future changeAppName( 47 | {required String appName, Iterable? platforms}) async { 48 | var oldName = await fileRepository.getCurrentPubSpecName(appName); 49 | if (platforms!.isEmpty || platforms.contains(Platform.ios)) { 50 | await fileRepository.myChangeIosAppName(appName); 51 | } 52 | if (platforms.isEmpty || platforms.contains(Platform.macOS)) { 53 | await fileRepository.myChangeMacOsAppName(appName); 54 | } 55 | if (platforms.isEmpty || platforms.contains(Platform.android)) { 56 | await fileRepository.myChangeAndroidAppName(appName); 57 | } 58 | changeFilesImports(appName, oldName!); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_create 2 | description: Create flutter project from templates e.g github. A standalone solution to issue https://github.com/flutter/flutter/issues/15279 3 | version: 1.0.4 4 | homepage: https://github.com/odunboye/flutter_create 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | 9 | executables: 10 | flutter_create: 11 | 12 | dependencies: 13 | path: ^1.8.0 14 | rename: ^2.0.1 15 | args: ^2.3.0 16 | 17 | dev_dependencies: 18 | lints: ^1.0.1 19 | test: ^1.19.4 20 | -------------------------------------------------------------------------------- /test/flutter_create_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:test/test.dart'; 2 | 3 | void main() { 4 | group('A group of tests', () {}); 5 | } 6 | --------------------------------------------------------------------------------