update to satisfy null safety

This commit is contained in:
Wenqi Li 2021-03-30 22:30:21 +08:00
parent 7f3911d89b
commit 184b728f12
11 changed files with 74 additions and 131 deletions

View File

@ -10,78 +10,32 @@ project 'Runner', {
'Release' => :release, 'Release' => :release,
} }
def parse_KV_file(file, separator='=') def flutter_root
file_abs_path = File.expand_path(file) generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
if !File.exists? file_abs_path unless File.exist?(generated_xcode_build_settings_path)
return []; raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end end
generated_key_values = {}
skip_line_start_symbols = ["#", "/"] File.foreach(generated_xcode_build_settings_path) do |line|
File.foreach(file_abs_path) do |line| matches = line.match(/FLUTTER_ROOT\=(.*)/)
next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } return matches[1].strip if matches
plugin = line.split(pattern=separator)
if plugin.length == 2
podname = plugin[0].strip()
path = plugin[1].strip()
podpath = File.expand_path("#{path}", file_abs_path)
generated_key_values[podname] = podpath
else
puts "Invalid plugin specification: #{line}"
end
end end
generated_key_values raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do target 'Runner' do
use_frameworks! use_frameworks!
use_modular_headers! use_modular_headers!
# Flutter Pod flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
copied_flutter_dir = File.join(__dir__, 'Flutter')
copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
# Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
# That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
# CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
unless File.exist?(generated_xcode_build_settings_path)
raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR'];
unless File.exist?(copied_framework_path)
FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
end
unless File.exist?(copied_podspec_path)
FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
end
end
# Keep pod path relative so it can be checked into Podfile.lock.
pod 'Flutter', :path => 'Flutter'
# Plugin Pods
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
# referring to absolute paths on developers' machines.
system('rm -rf .symlinks')
system('mkdir -p .symlinks/plugins')
plugin_pods = parse_KV_file('../.flutter-plugins')
plugin_pods.each do |name, path|
symlink = File.join('.symlinks', 'plugins', name)
File.symlink(path, symlink)
pod name, :path => File.join(symlink, 'ios')
end
end end
post_install do |installer| post_install do |installer|
installer.pods_project.targets.each do |target| installer.pods_project.targets.each do |target|
target.build_configurations.each do |config| flutter_additional_ios_build_settings(target)
config.build_settings['ENABLE_BITCODE'] = 'NO'
end
end end
end end

View File

@ -1,20 +1,17 @@
PODS: PODS:
- Flutter (1.0.0) - Flutter (1.0.0)
- "permission_handler (5.0.1+1)": - "permission_handler (5.1.0+2)":
- Flutter - Flutter
- photo_gallery (0.0.1): - photo_gallery (0.0.1):
- Flutter - Flutter
- video_player (0.0.1): - video_player (0.0.1):
- Flutter - Flutter
- video_player_web (0.0.1):
- Flutter
DEPENDENCIES: DEPENDENCIES:
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- permission_handler (from `.symlinks/plugins/permission_handler/ios`) - permission_handler (from `.symlinks/plugins/permission_handler/ios`)
- photo_gallery (from `.symlinks/plugins/photo_gallery/ios`) - photo_gallery (from `.symlinks/plugins/photo_gallery/ios`)
- video_player (from `.symlinks/plugins/video_player/ios`) - video_player (from `.symlinks/plugins/video_player/ios`)
- video_player_web (from `.symlinks/plugins/video_player_web/ios`)
EXTERNAL SOURCES: EXTERNAL SOURCES:
Flutter: Flutter:
@ -25,16 +22,13 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/photo_gallery/ios" :path: ".symlinks/plugins/photo_gallery/ios"
video_player: video_player:
:path: ".symlinks/plugins/video_player/ios" :path: ".symlinks/plugins/video_player/ios"
video_player_web:
:path: ".symlinks/plugins/video_player_web/ios"
SPEC CHECKSUMS: SPEC CHECKSUMS:
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c
permission_handler: eac8e15b4a1a3fba55b761d19f3f4e6b005d15b6 permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0
photo_gallery: 9f95e57747cd22c10676ece3660d1ffe6c603ee5 photo_gallery: 9f95e57747cd22c10676ece3660d1ffe6c603ee5
video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e
video_player_web: da8cadb8274ed4f8dbee8d7171b420dedd437ce7
PODFILE CHECKSUM: c34e2287a9ccaa606aeceab922830efb9a6ff69a PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
COCOAPODS: 1.9.3 COCOAPODS: 1.10.1

View File

@ -261,17 +261,12 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputPaths = ( inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
"${PODS_ROOT}/../Flutter/Flutter.framework",
"${BUILT_PRODUCTS_DIR}/photo_gallery/photo_gallery.framework",
"${BUILT_PRODUCTS_DIR}/video_player/video_player.framework",
); );
name = "[CP] Embed Pods Frameworks"; name = "[CP] Embed Pods Frameworks";
outputPaths = ( outputFileListPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/photo_gallery.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player.framework",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
@ -314,7 +309,6 @@
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = { 249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
@ -391,7 +385,6 @@
}; };
97C147031CF9000F007C117D /* Debug */ = { 97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;
@ -447,7 +440,6 @@
}; };
97C147041CF9000F007C117D /* Release */ = { 97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NONNULL = YES;

View File

@ -2,6 +2,6 @@
<Workspace <Workspace
version = "1.0"> version = "1.0">
<FileRef <FileRef
location = "group:Runner.xcodeproj"> location = "self:">
</FileRef> </FileRef>
</Workspace> </Workspace>

View File

@ -18,7 +18,7 @@ class MyApp extends StatefulWidget {
} }
class _MyAppState extends State<MyApp> { class _MyAppState extends State<MyApp> {
List<Album> _albums; List<Album>? _albums;
bool _loading = false; bool _loading = false;
@override @override
@ -150,7 +150,7 @@ class AlbumPage extends StatefulWidget {
} }
class AlbumPageState extends State<AlbumPage> { class AlbumPageState extends State<AlbumPage> {
List<Medium> _media; List<Medium>? _media;
@override @override
void initState() { void initState() {
@ -213,7 +213,7 @@ class ViewerPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
DateTime date = medium.creationDate ?? medium.modifiedDate; DateTime? date = medium.creationDate ?? medium.modifiedDate;
return MaterialApp( return MaterialApp(
home: Scaffold( home: Scaffold(
appBar: AppBar( appBar: AppBar(
@ -221,7 +221,7 @@ class ViewerPage extends StatelessWidget {
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
icon: Icon(Icons.arrow_back_ios), icon: Icon(Icons.arrow_back_ios),
), ),
title: Text(date?.toLocal().toString()), title: date != null ? Text(date.toLocal().toString()) : null,
), ),
body: Container( body: Container(
alignment: Alignment.center, alignment: Alignment.center,
@ -244,7 +244,7 @@ class VideoProvider extends StatefulWidget {
final String mediumId; final String mediumId;
const VideoProvider({ const VideoProvider({
@required this.mediumId, required this.mediumId,
}); });
@override @override
@ -252,12 +252,12 @@ class VideoProvider extends StatefulWidget {
} }
class _VideoProviderState extends State<VideoProvider> { class _VideoProviderState extends State<VideoProvider> {
VideoPlayerController _controller; VideoPlayerController? _controller;
File _file; File? _file;
@override @override
void initState() { void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance?.addPostFrameCallback((_) {
initAsync(); initAsync();
}); });
super.initState(); super.initState();
@ -266,8 +266,8 @@ class _VideoProviderState extends State<VideoProvider> {
Future<void> initAsync() async { Future<void> initAsync() async {
try { try {
_file = await PhotoGallery.getFile(mediumId: widget.mediumId); _file = await PhotoGallery.getFile(mediumId: widget.mediumId);
_controller = VideoPlayerController.file(_file); _controller = VideoPlayerController.file(_file!);
_controller.initialize().then((_) { _controller?.initialize().then((_) {
// Ensure the first frame is shown after the video is initialized, even before the play button has been pressed. // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
setState(() {}); setState(() {});
}); });
@ -278,25 +278,25 @@ class _VideoProviderState extends State<VideoProvider> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return _controller == null || !_controller.value.initialized return _controller == null || !_controller!.value.isInitialized
? Container() ? Container()
: Column( : Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
AspectRatio( AspectRatio(
aspectRatio: _controller.value.aspectRatio, aspectRatio: _controller!.value.aspectRatio,
child: VideoPlayer(_controller), child: VideoPlayer(_controller!),
), ),
FlatButton( FlatButton(
onPressed: () { onPressed: () {
setState(() { setState(() {
_controller.value.isPlaying _controller!.value.isPlaying
? _controller.pause() ? _controller!.pause()
: _controller.play(); : _controller!.play();
}); });
}, },
child: Icon( child: Icon(
_controller.value.isPlaying ? Icons.pause : Icons.play_arrow, _controller!.value.isPlaying ? Icons.pause : Icons.play_arrow,
), ),
), ),
], ],

View File

@ -6,7 +6,7 @@ description: Demonstrates how to use the photo_gallery plugin.
# publish_to: 'none' # Remove this line if you wish to publish to pub.dev # publish_to: 'none' # Remove this line if you wish to publish to pub.dev
environment: environment:
sdk: ">=2.7.0 <3.0.0" sdk: ">=2.12.0 <3.0.0"
dependencies: dependencies:
flutter: flutter:
@ -22,10 +22,10 @@ dependencies:
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.3 cupertino_icons: ^1.0.2
transparent_image: ^1.0.0 transparent_image: ^2.0.0
permission_handler: ^5.0.1+1 permission_handler: ^6.1.1
video_player: ^0.10.12 video_player: ^2.1.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

@ -67,7 +67,7 @@ class PhotoGallery {
MediumType? mediumType, MediumType? mediumType,
int? width, int? width,
int? height, int? height,
bool? highQuality, bool? highQuality = false,
}) async { }) async {
final bytes = await _channel.invokeMethod('getThumbnail', { final bytes = await _channel.invokeMethod('getThumbnail', {
'mediumId': mediumId, 'mediumId': mediumId,
@ -76,7 +76,7 @@ class PhotoGallery {
'height': height, 'height': height,
'highQuality': highQuality, 'highQuality': highQuality,
}); });
return bytes; return new List<int>.from(bytes);
} }
/// Get album thumbnail by album id /// Get album thumbnail by album id
@ -85,7 +85,7 @@ class PhotoGallery {
MediumType? mediumType, MediumType? mediumType,
int? width, int? width,
int? height, int? height,
bool? highQuality, bool? highQuality = false,
}) async { }) async {
final bytes = await _channel.invokeMethod('getAlbumThumbnail', { final bytes = await _channel.invokeMethod('getAlbumThumbnail', {
'albumId': albumId, 'albumId': albumId,
@ -94,7 +94,7 @@ class PhotoGallery {
'height': height, 'height': height,
'highQuality': highQuality, 'highQuality': highQuality,
}); });
return bytes; return new List<int>.from(bytes);
} }
/// get medium file by medium id /// get medium file by medium id

View File

@ -7,7 +7,7 @@ class ThumbnailProvider extends ImageProvider<ThumbnailProvider> {
this.mediumType, this.mediumType,
this.height, this.height,
this.width, this.width,
this.highQuality, this.highQuality = false,
}); });
final String mediumId; final String mediumId;

View File

@ -37,6 +37,7 @@ void main() {
MediaPage expected = Generator.generateMediaPage( MediaPage expected = Generator.generateMediaPage(
album: allAlbum, album: allAlbum,
mediumType: mediumType, mediumType: mediumType,
total: allAlbum.count,
skip: skip, skip: skip,
take: take, take: take,
); );

View File

@ -3,7 +3,8 @@ import 'dart:io';
import 'package:photo_gallery/photo_gallery.dart'; import 'package:photo_gallery/photo_gallery.dart';
class Generator { class Generator {
static dynamic generateAlbumsJson({MediumType mediumType = MediumType.image}) { static dynamic generateAlbumsJson(
{MediumType? mediumType = MediumType.image}) {
return [ return [
{ {
"id": "__ALL__", "id": "__ALL__",
@ -20,7 +21,7 @@ class Generator {
]; ];
} }
static List<Album> generateAlbums({required MediumType mediumType}) { static List<Album> generateAlbums({MediumType? mediumType}) {
return Generator.generateAlbumsJson(mediumType: mediumType) return Generator.generateAlbumsJson(mediumType: mediumType)
.map<Album>((x) => Album.fromJson(x)) .map<Album>((x) => Album.fromJson(x))
.toList(); .toList();
@ -28,8 +29,8 @@ class Generator {
static dynamic generateMediaPageJson({ static dynamic generateMediaPageJson({
required String albumId, required String albumId,
required MediumType mediumType, MediumType? mediumType,
required int total, int total = 10,
int? skip, int? skip,
int? take, int? take,
}) { }) {
@ -53,7 +54,7 @@ class Generator {
static dynamic generateMediaJson({ static dynamic generateMediaJson({
required String mediumId, required String mediumId,
required MediumType mediumType, MediumType? mediumType,
}) { }) {
return { return {
"id": mediumId, "id": mediumId,
@ -69,14 +70,15 @@ class Generator {
static MediaPage generateMediaPage({ static MediaPage generateMediaPage({
required Album album, required Album album,
required MediumType mediumType, MediumType? mediumType,
required int total,
int? skip, int? skip,
int? take, int? take,
}) { }) {
dynamic json = generateMediaPageJson( dynamic json = generateMediaPageJson(
albumId: album.id, albumId: album.id,
mediumType: mediumType, mediumType: mediumType,
total: album.count, total: total,
skip: skip, skip: skip,
take: take, take: take,
); );
@ -85,7 +87,7 @@ class Generator {
static Medium generateMedia({ static Medium generateMedia({
required String mediumId, required String mediumId,
required MediumType mediumType, MediumType? mediumType,
}) { }) {
return Medium.fromJson( return Medium.fromJson(
generateMediaJson(mediumId: mediumId, mediumType: mediumType), generateMediaJson(mediumId: mediumId, mediumType: mediumType),
@ -94,7 +96,7 @@ class Generator {
static List<int> generateMockThumbnail({ static List<int> generateMockThumbnail({
required String mediumId, required String mediumId,
required MediumType mediumType, MediumType? mediumType,
}) { }) {
return [1, 2, 3, 4, 5, 6, 7, 8, 9]; return [1, 2, 3, 4, 5, 6, 7, 8, 9];
} }
@ -107,14 +109,14 @@ class Generator {
static String generateFilePath({ static String generateFilePath({
required String mediumId, required String mediumId,
required MediumType mediumType, MediumType? mediumType,
}) { }) {
return "/path/to/file"; return "/path/to/file";
} }
static File generateFile({ static File generateFile({
required String mediumId, required String mediumId,
required MediumType mediumType, MediumType? mediumType,
}) { }) {
return File(generateFilePath(mediumId: mediumId, mediumType: mediumType)); return File(generateFilePath(mediumId: mediumId, mediumType: mediumType));
} }

View File

@ -6,32 +6,32 @@ import 'generator.dart';
Future<dynamic> mockMethodCallHandler(MethodCall call) async { Future<dynamic> mockMethodCallHandler(MethodCall call) async {
if (call.method == "listAlbums") { if (call.method == "listAlbums") {
MediumType mediumType = jsonToMediumType(call.arguments['mediumType']); MediumType? mediumType = jsonToMediumType(call.arguments['mediumType']);
dynamic albums = Generator.generateAlbumsJson(mediumType: mediumType); dynamic albums = Generator.generateAlbumsJson(mediumType: mediumType);
return albums; return albums;
} else if (call.method == "listMedia") { } else if (call.method == "listMedia") {
String albumId = call.arguments['albumId']; String albumId = call.arguments['albumId'];
MediumType? mediumType = jsonToMediumType(call.arguments['mediumType']); MediumType? mediumType = jsonToMediumType(call.arguments['mediumType']);
int total = call.arguments['total']; int? total = call.arguments['total'];
int skip = call.arguments['skip']; int? skip = call.arguments['skip'];
int take = call.arguments['take']; int? take = call.arguments['take'];
dynamic mediaPage = Generator.generateMediaPageJson( dynamic mediaPage = Generator.generateMediaPageJson(
albumId: albumId, albumId: albumId,
mediumType: mediumType, mediumType: mediumType,
total: total, total: total ?? 10,
skip: skip, skip: skip,
take: take, take: take,
); );
return mediaPage; return mediaPage;
} else if (call.method == "getMedium") { } else if (call.method == "getMedium") {
String mediumId = call.arguments['mediumId']; String mediumId = call.arguments['mediumId'];
MediumType mediumType = jsonToMediumType(call.arguments['mediumType']); MediumType? mediumType = jsonToMediumType(call.arguments['mediumType']);
dynamic media = dynamic media =
Generator.generateMediaJson(mediumId: mediumId, mediumType: mediumType); Generator.generateMediaJson(mediumId: mediumId, mediumType: mediumType);
return media; return media;
} else if (call.method == "getThumbnail") { } else if (call.method == "getThumbnail") {
String mediumId = call.arguments['mediumId']; String mediumId = call.arguments['mediumId'];
MediumType mediumType = jsonToMediumType(call.arguments['mediumType']); MediumType? mediumType = jsonToMediumType(call.arguments['mediumType']);
dynamic thumbnail = Generator.generateMockThumbnail( dynamic thumbnail = Generator.generateMockThumbnail(
mediumId: mediumId, mediumType: mediumType); mediumId: mediumId, mediumType: mediumType);
return thumbnail; return thumbnail;
@ -41,7 +41,7 @@ Future<dynamic> mockMethodCallHandler(MethodCall call) async {
return thumbnail; return thumbnail;
} else if (call.method == "getFile") { } else if (call.method == "getFile") {
String mediumId = call.arguments['mediumId']; String mediumId = call.arguments['mediumId'];
MediumType mediumType = jsonToMediumType(call.arguments['mediumType']); MediumType? mediumType = jsonToMediumType(call.arguments['mediumType']);
dynamic path = dynamic path =
Generator.generateFilePath(mediumId: mediumId, mediumType: mediumType); Generator.generateFilePath(mediumId: mediumId, mediumType: mediumType);
return path; return path;