Compare commits
8
Commits
a47af8c3ad
...
v1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66c03ee245 | ||
|
|
a3ac007759 | ||
|
|
8ec6df2be5 | ||
|
|
5ec4db151a | ||
|
|
525f58c150 | ||
|
|
0a02620564 | ||
|
|
78a33cd4f1 | ||
|
|
e8610b7aed |
No files matched your search
@@ -386,15 +386,29 @@ final class PipShadowCoordinator: NSObject, AVPictureInPictureControllerDelegate
|
|||||||
guard call.method == "shareText",
|
guard call.method == "shareText",
|
||||||
let arguments = call.arguments as? [String: Any],
|
let arguments = call.arguments as? [String: Any],
|
||||||
let text = arguments["text"] as? String,
|
let text = arguments["text"] as? String,
|
||||||
!text.isEmpty,
|
!text.isEmpty else {
|
||||||
let controller = self?.window?.rootViewController else {
|
|
||||||
result(FlutterError(code: "share_unavailable", message: "Share sheet is unavailable", details: nil))
|
result(FlutterError(code: "share_unavailable", message: "Share sheet is unavailable", details: nil))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let activity = UIActivityViewController(activityItems: [text], applicationActivities: nil)
|
DispatchQueue.main.async {
|
||||||
activity.popoverPresentationController?.sourceView = controller.view
|
guard let controller = self?.sharePresenter() else {
|
||||||
activity.completionWithItemsHandler = { _, _, _, _ in result(nil) }
|
result(FlutterError(code: "share_unavailable", message: "Share sheet is unavailable", details: nil))
|
||||||
controller.present(activity, animated: true)
|
return
|
||||||
|
}
|
||||||
|
let activity = UIActivityViewController(activityItems: [text], applicationActivities: nil)
|
||||||
|
if let popover = activity.popoverPresentationController {
|
||||||
|
popover.sourceView = controller.view
|
||||||
|
popover.sourceRect = CGRect(
|
||||||
|
x: controller.view.bounds.midX,
|
||||||
|
y: controller.view.bounds.midY,
|
||||||
|
width: 0,
|
||||||
|
height: 0
|
||||||
|
)
|
||||||
|
popover.permittedArrowDirections = []
|
||||||
|
}
|
||||||
|
activity.completionWithItemsHandler = { _, _, _, _ in result(nil) }
|
||||||
|
controller.present(activity, animated: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let thumbnailChannel = FlutterMethodChannel(
|
let thumbnailChannel = FlutterMethodChannel(
|
||||||
@@ -459,6 +473,34 @@ final class PipShadowCoordinator: NSObject, AVPictureInPictureControllerDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 当前前台 Scene 的根控制器之上,最适合展示系统分享面板的控制器。
|
||||||
|
private func sharePresenter() -> UIViewController? {
|
||||||
|
let keyWindow = UIApplication.shared.connectedScenes
|
||||||
|
.compactMap { $0 as? UIWindowScene }
|
||||||
|
.first { $0.activationState == .foregroundActive }?
|
||||||
|
.windows.first { $0.isKeyWindow }
|
||||||
|
guard let rootController = keyWindow?.rootViewController else { return nil }
|
||||||
|
let presenter = topPresentedController(from: rootController)
|
||||||
|
guard presenter.viewIfLoaded?.window != nil else { return nil }
|
||||||
|
return presenter
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 解析已展示页面、导航栈和标签页中的当前可见控制器。
|
||||||
|
private func topPresentedController(from controller: UIViewController) -> UIViewController {
|
||||||
|
if let presented = controller.presentedViewController {
|
||||||
|
return topPresentedController(from: presented)
|
||||||
|
}
|
||||||
|
if let navigation = controller as? UINavigationController,
|
||||||
|
let visible = navigation.visibleViewController {
|
||||||
|
return topPresentedController(from: visible)
|
||||||
|
}
|
||||||
|
if let tabs = controller as? UITabBarController,
|
||||||
|
let selected = tabs.selectedViewController {
|
||||||
|
return topPresentedController(from: selected)
|
||||||
|
}
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
/// iOS 画中画:影子播放器通道,与 Android 共用同一 channel 名。
|
/// iOS 画中画:影子播放器通道,与 Android 共用同一 channel 名。
|
||||||
private func registerPipChannel(_ engineBridge: FlutterImplicitEngineBridge) {
|
private func registerPipChannel(_ engineBridge: FlutterImplicitEngineBridge) {
|
||||||
let channel = FlutterMethodChannel(
|
let channel = FlutterMethodChannel(
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// Author: fruit.com.cn
|
// Author: fruit.com.cn
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
import 'dev_config.dart';
|
import 'dev_config.dart';
|
||||||
import 'prod_config.dart';
|
import 'prod_config.dart';
|
||||||
|
|
||||||
@@ -9,12 +11,10 @@ import 'prod_config.dart';
|
|||||||
/// - `dev_config.dart`:[DevConfig](本机联调)
|
/// - `dev_config.dart`:[DevConfig](本机联调)
|
||||||
/// - `prod_config.dart`:[ProdConfig](生产)
|
/// - `prod_config.dart`:[ProdConfig](生产)
|
||||||
///
|
///
|
||||||
/// 切换环境只改下面这一个布尔值,不用传任何构建参数、也不做网络探测。
|
/// Debug 使用开发环境,Release 自动使用生产环境,不做网络探测。
|
||||||
abstract final class AppConfig {
|
abstract final class AppConfig {
|
||||||
/// **环境开关**:true 用开发环境,false 用生产环境。
|
/// **环境开关**:Debug 使用开发环境,Release 使用生产环境。
|
||||||
///
|
static const bool useDevConfig = !kReleaseMode;
|
||||||
/// 打生产包前必须改成 false;release 构建若仍为 true 会在启动时直接报错。
|
|
||||||
static const bool useDevConfig = true;
|
|
||||||
|
|
||||||
/// 当前选中的环境地址。
|
/// 当前选中的环境地址。
|
||||||
static String get baseUrl =>
|
static String get baseUrl =>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const double _progressHeight = 2;
|
|||||||
const double _progressHitHeight = 16;
|
const double _progressHitHeight = 16;
|
||||||
const double _pageHorizontal = 20;
|
const double _pageHorizontal = 20;
|
||||||
const double _railRight = 14;
|
const double _railRight = 14;
|
||||||
|
const double _railShiftRight = 8;
|
||||||
const double _railGap = 12;
|
const double _railGap = 12;
|
||||||
const double _railWidth = 56;
|
const double _railWidth = 56;
|
||||||
const double _infoWatchAllGap = 2;
|
const double _infoWatchAllGap = 2;
|
||||||
@@ -234,7 +235,10 @@ class _HomeFeedOverlayState extends State<HomeFeedOverlay> {
|
|||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
bottom: _railBottomOffset,
|
bottom: _railBottomOffset,
|
||||||
),
|
),
|
||||||
child: _rail(),
|
child: Transform.translate(
|
||||||
|
offset: const Offset(_railShiftRight, 0),
|
||||||
|
child: _rail(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// Author: fruit.com.cn
|
||||||
|
|
||||||
|
// 应用环境选择测试。
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:fruitshorts/core/config/app_config.dart';
|
||||||
|
import 'package:fruitshorts/core/config/dev_config.dart';
|
||||||
|
import 'package:fruitshorts/core/config/prod_config.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('should_use_environment_matching_build_mode', () {
|
||||||
|
expect(AppConfig.useDevConfig, !kReleaseMode);
|
||||||
|
expect(
|
||||||
|
AppConfig.baseUrl,
|
||||||
|
kReleaseMode ? ProdConfig.baseUrl : DevConfig.baseUrl,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -373,7 +373,7 @@ void main() {
|
|||||||
expect(
|
expect(
|
||||||
tester.getRect(find.byKey(const Key('home-feed-action-rail'))).right,
|
tester.getRect(find.byKey(const Key('home-feed-action-rail'))).right,
|
||||||
closeTo(
|
closeTo(
|
||||||
tester.getRect(find.byKey(const Key('home-feed-gesture'))).right - 14,
|
tester.getRect(find.byKey(const Key('home-feed-gesture'))).right - 6,
|
||||||
0.1,
|
0.1,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in new issue
Block a user