尝试消除ios播放页全屏底部空白条
添加flutter_launcher_icons生成ios平台图标
1
.vscode/settings.json
vendored
|
|
@ -23,6 +23,7 @@
|
||||||
"Vaani"
|
"Vaani"
|
||||||
],
|
],
|
||||||
"dart.flutterSdkPath": ".fvm/versions/3.32.0",
|
"dart.flutterSdkPath": ".fvm/versions/3.32.0",
|
||||||
|
"dart.lineLength": 80,
|
||||||
"files.exclude": {
|
"files.exclude": {
|
||||||
"**/*.freezed.dart": true,
|
"**/*.freezed.dart": true,
|
||||||
"**/*.g.dart": true
|
"**/*.g.dart": true
|
||||||
|
|
|
||||||
|
|
@ -428,7 +428,7 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
|
@ -485,7 +485,7 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
|
|
||||||
|
|
@ -1,122 +1 @@
|
||||||
{
|
{"images":[{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-20x20@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-20x20@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-29x29@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-29x29@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-29x29@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-40x40@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-40x40@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "60x60",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-60x60@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "60x60",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "Icon-App-60x60@3x.png",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-20x20@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "20x20",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-20x20@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-29x29@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "29x29",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-29x29@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-40x40@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "40x40",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-40x40@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "76x76",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-76x76@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "76x76",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-76x76@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "83.5x83.5",
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"filename" : "Icon-App-83.5x83.5@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"size" : "1024x1024",
|
|
||||||
"idiom" : "ios-marketing",
|
|
||||||
"filename" : "Icon-App-1024x1024@1x.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 220 KiB |
|
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 983 B |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 4 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 762 B After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 16 KiB |
|
|
@ -144,7 +144,7 @@ class PlayerWhenMinimized extends HookConsumerWidget {
|
||||||
// value: (progress.data ?? Duration.zero).inSeconds /
|
// value: (progress.data ?? Duration.zero).inSeconds /
|
||||||
// player.book!.duration.inSeconds,
|
// player.book!.duration.inSeconds,
|
||||||
value: (progress.data ?? Duration.zero).inSeconds /
|
value: (progress.data ?? Duration.zero).inSeconds /
|
||||||
player.duration!.inSeconds,
|
(player.duration?.inSeconds ?? 1),
|
||||||
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||||
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
|
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,10 @@ class ScaffoldWithNavBar extends HookConsumerWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final size = MediaQuery.of(context).size;
|
||||||
final playerProgress = ref.watch(playerHeightProvider);
|
final playerProgress = ref.watch(playerHeightProvider);
|
||||||
final isMobile = Platform.isFuchsia || Platform.isAndroid || Platform.isIOS;
|
final isMobile = Platform.isAndroid || Platform.isIOS || Platform.isFuchsia;
|
||||||
|
final isVertical = size.height > size.width;
|
||||||
onBackButtonPressed() async {
|
onBackButtonPressed() async {
|
||||||
final isPlayerExpanded = playerProgress != playerMinHeight;
|
final isPlayerExpanded = playerProgress != playerMinHeight;
|
||||||
|
|
||||||
|
|
@ -93,11 +95,14 @@ class ScaffoldWithNavBar extends HookConsumerWidget {
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
body: Stack(
|
body: Stack(
|
||||||
children: [
|
children: [
|
||||||
isMobile ? navigationShell : buildNavLeft(context, ref),
|
isMobile && isVertical
|
||||||
|
? navigationShell
|
||||||
|
: buildNavLeft(context, ref),
|
||||||
const AudiobookPlayer(),
|
const AudiobookPlayer(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
bottomNavigationBar: isMobile ? buildNavBottom(context, ref) : null,
|
bottomNavigationBar:
|
||||||
|
isMobile && isVertical ? buildNavBottom(context, ref) : null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -165,63 +170,65 @@ class ScaffoldWithNavBar extends HookConsumerWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildNavBottom(BuildContext context, WidgetRef ref) {
|
Widget? buildNavBottom(BuildContext context, WidgetRef ref) {
|
||||||
// playerExpandProgress is used to animate bottom navigation bar to opacity 0 and slide down when player is expanded
|
final size = MediaQuery.of(context).size;
|
||||||
// final playerProgress =
|
|
||||||
// useValueListenable(ref.watch(playerExpandProgressNotifierProvider));
|
|
||||||
final playerProgress = ref.watch(playerHeightProvider);
|
final playerProgress = ref.watch(playerHeightProvider);
|
||||||
final playerMaxHeight = MediaQuery.of(context).size.height;
|
final playerMaxHeight = size.height;
|
||||||
var percentExpandedMiniPlayer = (playerProgress - playerMinHeight) /
|
var percentExpandedMiniPlayer = (playerProgress - playerMinHeight) /
|
||||||
(playerMaxHeight - playerMinHeight);
|
(playerMaxHeight - playerMinHeight);
|
||||||
// Clamp the value between 0 and 1
|
// Clamp the value between 0 and 1
|
||||||
percentExpandedMiniPlayer = percentExpandedMiniPlayer.clamp(0.0, 1.0);
|
percentExpandedMiniPlayer = percentExpandedMiniPlayer.clamp(0.0, 1.0);
|
||||||
return Opacity(
|
return percentExpandedMiniPlayer == 1
|
||||||
// Opacity is interpolated from 1 to 0 when player is expanded
|
? Opacity(
|
||||||
opacity: 1 - percentExpandedMiniPlayer,
|
// Opacity is interpolated from 1 to 0 when player is expanded
|
||||||
child: NavigationBar(
|
opacity: 1 - percentExpandedMiniPlayer,
|
||||||
elevation: 0.0,
|
child: NavigationBar(
|
||||||
height: bottomBarHeight * (1 - percentExpandedMiniPlayer),
|
elevation: 0.0,
|
||||||
|
height: bottomBarHeight * (1 - percentExpandedMiniPlayer),
|
||||||
|
|
||||||
// TODO: get destinations from the navigationShell
|
// TODO: get destinations from the navigationShell
|
||||||
// Here, the items of BottomNavigationBar are hard coded. In a real
|
// Here, the items of BottomNavigationBar are hard coded. In a real
|
||||||
// world scenario, the items would most likely be generated from the
|
// world scenario, the items would most likely be generated from the
|
||||||
// branches of the shell route, which can be fetched using
|
// branches of the shell route, which can be fetched using
|
||||||
// `navigationShell.route.branches`.
|
// `navigationShell.route.branches`.
|
||||||
destinations: _navigationItems(context).map((item) {
|
destinations: _navigationItems(context).map((item) {
|
||||||
final isDestinationLibrary = item.name == S.of(context).library;
|
final isDestinationLibrary = item.name == S.of(context).library;
|
||||||
var currentLibrary = ref.watch(currentLibraryProvider).valueOrNull;
|
var currentLibrary =
|
||||||
final libraryIcon = AbsIcons.getIconByName(
|
ref.watch(currentLibraryProvider).valueOrNull;
|
||||||
currentLibrary?.icon,
|
final libraryIcon = AbsIcons.getIconByName(
|
||||||
);
|
currentLibrary?.icon,
|
||||||
final destinationWidget = NavigationDestination(
|
);
|
||||||
icon: Icon(
|
final destinationWidget = NavigationDestination(
|
||||||
isDestinationLibrary ? libraryIcon ?? item.icon : item.icon,
|
icon: Icon(
|
||||||
|
isDestinationLibrary ? libraryIcon ?? item.icon : item.icon,
|
||||||
|
),
|
||||||
|
selectedIcon: Icon(
|
||||||
|
isDestinationLibrary
|
||||||
|
? libraryIcon ?? item.activeIcon
|
||||||
|
: item.activeIcon,
|
||||||
|
),
|
||||||
|
label: isDestinationLibrary
|
||||||
|
? currentLibrary?.name ?? item.name
|
||||||
|
: item.name,
|
||||||
|
tooltip: item.tooltip,
|
||||||
|
);
|
||||||
|
if (isDestinationLibrary) {
|
||||||
|
return GestureDetector(
|
||||||
|
onSecondaryTap: () => showLibrarySwitcher(context, ref),
|
||||||
|
onDoubleTap: () => showLibrarySwitcher(context, ref),
|
||||||
|
child:
|
||||||
|
destinationWidget, // Wrap the actual NavigationDestination
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Return the unwrapped destination for other items
|
||||||
|
return destinationWidget;
|
||||||
|
}
|
||||||
|
}).toList(),
|
||||||
|
selectedIndex: navigationShell.currentIndex,
|
||||||
|
onDestinationSelected: (int index) => _onTap(context, index, ref),
|
||||||
),
|
),
|
||||||
selectedIcon: Icon(
|
)
|
||||||
isDestinationLibrary
|
: null;
|
||||||
? libraryIcon ?? item.activeIcon
|
|
||||||
: item.activeIcon,
|
|
||||||
),
|
|
||||||
label: isDestinationLibrary
|
|
||||||
? currentLibrary?.name ?? item.name
|
|
||||||
: item.name,
|
|
||||||
tooltip: item.tooltip,
|
|
||||||
);
|
|
||||||
if (isDestinationLibrary) {
|
|
||||||
return GestureDetector(
|
|
||||||
onSecondaryTap: () => showLibrarySwitcher(context, ref),
|
|
||||||
onDoubleTap: () => showLibrarySwitcher(context, ref),
|
|
||||||
child: destinationWidget, // Wrap the actual NavigationDestination
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Return the unwrapped destination for other items
|
|
||||||
return destinationWidget;
|
|
||||||
}
|
|
||||||
}).toList(),
|
|
||||||
selectedIndex: navigationShell.currentIndex,
|
|
||||||
onDestinationSelected: (int index) => _onTap(context, index, ref),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<_NavigationItem> _navigationItems(BuildContext context) {
|
List<_NavigationItem> _navigationItems(BuildContext context) {
|
||||||
|
|
|
||||||
400
pubspec.lock
|
|
@ -108,6 +108,7 @@ dev_dependencies:
|
||||||
json_serializable: ^6.8.0
|
json_serializable: ^6.8.0
|
||||||
riverpod_generator: ^2.4.2
|
riverpod_generator: ^2.4.2
|
||||||
riverpod_lint: ^2.3.10
|
riverpod_lint: ^2.3.10
|
||||||
|
flutter_launcher_icons: "^0.14.4"
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
@ -156,3 +157,9 @@ flutter:
|
||||||
- asset: assets/fonts/AbsIcons.ttf
|
- asset: assets/fonts/AbsIcons.ttf
|
||||||
flutter_intl:
|
flutter_intl:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
|
||||||
|
flutter_launcher_icons:
|
||||||
|
android: false # 如果只为iOS配置,可关闭Android
|
||||||
|
ios: true
|
||||||
|
image_path: "assets/icon/logo.png" # 你的1024x1024源图标路径
|
||||||
|
remove_alpha_ios: true # 建议设为true,移除透明度,符合App Store审核要求[citation:6]
|
||||||