Flutter: iPhone X:BottomNavigationBar 的额外底部填充

创建于 2017-09-14  ·  18评论  ·  资料来源: flutter/flutter

重现步骤

在 iPhone X 上, BottomNavigationBar应该包括额外的底部填充以避免将BottomNavigationBarItem放置在访问主屏幕的指示器下方。

请参阅 Apple 的iPhone X 人机界面指南

取决于问题 #12098

例子:
screen shot 2017-09-12 at 15 21 34

颤振医生

[✓] Flutter (on Mac OS X 10.12.6 16G29, locale en-AU, channel master)
    • Flutter at /Users/cbracken/src/flutter/flutter
    • Framework revision dd7e313317 (51 minutes ago), 2017-09-14 12:28:21 -0700
    • Engine revision 57a1445a45
    • Tools Dart version 1.25.0-dev.11.0

[✓] Android toolchain - develop for Android devices (Android SDK 25.0.3)
    • Android SDK at /Users/cbracken/Library/Android/sdk
    • Platform android-25, build-tools 25.0.3
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_112-release-b06)

[✓] iOS toolchain - develop for iOS devices (Xcode 9.0)
    • Xcode at /Applications/Xcode9.0.app/Contents/Developer
    • Xcode 9.0, Build version 9A235
    • ios-deploy 1.9.2
    • CocoaPods version 1.3.1
framework platform-ios

最有用的评论

@najeira @passsy在上述提交中,您可以通过MediaQuery.of(context).padding获得 iPhone X 安全区域插图。 bottom填充为 iPhone X 主页指示器(如果存在)提供额外的底部填充。

所有18条评论

这应该基于 MediaQuery.of().padding,与 appbar 相同。

遇到同样的问题。 仍然没有找到检测iPhone X的方法。 我试过https://pub.dartlang.org/packages/device_info但模拟器总是返回iPhonemodel而不是 iPhone X

您可以通过将 BottomNavigationBar 包装在 SafeArea 小部件中来解决此问题,我不确定我们在为 iPhone X 实现该功能方面取得了哪些进展。 @cbracken可能知道。

我有一个补丁来修复底部栏和一个引擎补丁来添加支持以显示安全区域插图。 为了获得这些,我们需要将我们的构建基础设施/机器人升级到 Xcode 9。我已经提交了相应的票来安装它,并且一旦他们完成更新工作就会登陆。

我这样做如下:

final bool iphonex = MediaQuery.of(context).size.height >= 812.0;
final double bottomPadding = iphonex ? 16.0 : 0.0;
// add the padding to arround CupertinoTabBar

如果您还针对 android,您可能还希望在平台上键入它。

这是我目前的解决方法:

代码

import 'dart:io';

import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';

// workaround for iPhone X which draws navigation in the bottom of the screen.
// Wait until https://github.com/flutter/flutter/issues/12099 is fixed
class IPhoneXPadding extends Container {
  final Widget child;

  IPhoneXPadding({
    <strong i="7">@required</strong> this.child,
  });

  <strong i="8">@override</strong>
  Widget build(BuildContext context) {
    var mediaQueryData = MediaQuery.of(context);
    if (!_isIPhoneX(mediaQueryData)) {
      // fallback for all non iPhone X
      return child;
    }

    var homeIndicatorHeight =
    // TODO verify exact values
    mediaQueryData.orientation == Orientation.portrait ? 22.0 : 20.0;

    var outer = mediaQueryData.padding;
    var bottom = outer.bottom + homeIndicatorHeight;
    return new MediaQuery(data: new MediaQueryData(
        padding: new EdgeInsets.fromLTRB(
            outer.left, outer.top, outer.right, bottom)),
        child: child
    );
  }

  bool _isIPhoneX(MediaQueryData mediaQuery) {
    if (Platform.isIOS) {
      var size = mediaQuery.size;
      if (size.height == 812.0 || size.width == 812.0) {
        return true;
      }
    }
    return false;
  }
}

用法

  <strong i="12">@override</strong>
  Widget build(BuildContext context) {
    return new IPhoneXPadding(
      child: new Scaffold(
        bottomNavigationBar: _bottomNavBar,
        body: /*...*/,
      ),
    );
  }

结果

screen shot 2017-11-07 at 19 11 18

问题

Scaffold不会将BottomNavigationbar扩展到底部。 如果BottomNavigationbar不会投射此高度阴影,它可能看起来不错

小更新:我正在进行引擎工作以解决 #12895,这是获得 iPhone X 问题通用解决方案的先决条件。 完成后,我将清理底部导航的初步补丁(以及列表和网格图块)。

同时,如果您需要快速解决方法,可以将内联补丁应用到分支的底部导航。 您可能需要稍微调整一下数字(25.0、34.0)。 还公平地警告说,这是对上述初步补丁的快速改编(取决于即将发生的引擎更改),以便与基于启发式的手动 iPhone X 检查一起使用,所以我没有在本地尝试过这些确切的补丁; )

底部导航栏:

--- i/packages/flutter/lib/src/material/bottom_navigation_bar.dart
+++ w/packages/flutter/lib/src/material/bottom_navigation_bar.dart
@@ -435,6 +436,9 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
   <strong i="8">@override</strong>
   Widget build(BuildContext context) {
+    bool isIphoneX = ...;
+    double bottomPadding = isIphoneX ? 25.0 : 0.0;
     Color backgroundColor;
     switch (widget.type) {
       case BottomNavigationBarType.fixed:
@@ -452,7 +456,7 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
           ),
         ),
         new ConstrainedBox(
-          constraints: const BoxConstraints(minHeight: kBottomNavigationBarHeight),
+          constraints: new BoxConstraints(minHeight: kBottomNavigationBarHeight + bottomPadding),
           child: new Stack(
             children: <Widget>[
               new Positioned.fill(
@@ -464,7 +468,10 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
               ),
               new Material( // Splashes.
                 type: MaterialType.transparency,
-                child: _createContainer(_createTiles()),
+                child: new Padding(
+                  padding: new EdgeInsets.only(bottom: bottomPadding),
+                  child: _createContainer(_createTiles()),
+                ),
               ),
             ],
           ),

库比蒂诺标签栏:

--- i/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart
+++ w/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart
@@ -92,6 +92,7 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget {

   <strong i="12">@override</strong>
   Widget build(BuildContext context) {
+    bool isIphoneX = ...;
+    double bottomPadding = isIphoneX ? 34.0 : 0.0;
     Widget result = new DecoratedBox(
       decoration: new BoxDecoration(
         border: const Border(
@@ -105,7 +106,7 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget {
       ),
       // TODO(xster): allow icons-only versions of the tab bar too.
       child: new SizedBox(
-        height: _kTabBarHeight,
+        height: _kTabBarHeight + bottomPadding,
         child: IconTheme.merge( // Default with the inactive state.
           data: new IconThemeData(
             color: inactiveColor,
@@ -119,10 +120,13 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget {
               fontWeight: FontWeight.w500,
               color: inactiveColor,
             ),
-            child: new Row(
-              // Align bottom since we want the labels to be aligned.
-              crossAxisAlignment: CrossAxisAlignment.end,
-              children: _buildTabItems(),
+            child: new Padding(
+              padding: new EdgeInsets.only(bottom: bottomPadding),
+              child: new Row(
+                // Align bottom since we want the labels to be aligned.
+                crossAxisAlignment: CrossAxisAlignment.end,
+                children: _buildTabItems(),
+              ),
             ),
           ),
         ),

我应该澄清一下 - 垂直方向的附加底部填充为 34.0,水平方向为 21.0。 在这两种情况下,目前底部导航为负 9.0。

从 #13442 开始,这对于材质BottomNavigationBarCupertinoNavigationBar都固定在master $ 上。

@najeira @passsy在上述提交中,您可以通过MediaQuery.of(context).padding获得 iPhone X 安全区域插图。 bottom填充为 iPhone X 主页指示器(如果存在)提供额外的底部填充。

@passsy你对 iPhone X 的检测对我有用,你能分享一下你是如何想出 812 作为尺寸的吗?

bool _isIPhoneX(MediaQueryData mediaQuery) {
    if (Platform.isIOS) {
      var size = mediaQuery.size;
      if (size.height == 812.0 || size.width == 812.0) {
        return true;
      }
    }
    return false;
  }

@deborah-ufw 不再需要手动检测 iPhone X。 该框架现在包括对 iOS 安全区域插入的支持。 有关详细信息,请参阅SafeArea类。 如果您需要安全区域插图的像素值,而不是自动应用填充,则可以通过MediaQuery.of(context).padding它们。 .bottom用于 iPhone X 主页指示器安全区域高度, .left / .right用于横向模式下的凹槽(以及另一侧的对称插图), .top用于纵向模式下的状态栏/缺口。

作为奖励,这些将适用于所有未来的类似 iPhone X 的 iPhone 型号,以及 Android 手机上即将推出的任何“创意”显示器——我目前唯一知道的是 Essential Phone,它有一个相机缺口,但我相信还会有更多。

还是想知道这个数字是怎么来的。 :)

2018 年 2 月 12 日上午 11:39,Chris Bracken [email protected]写道:

@deborah-ufw https://github.com/deborah-ufw不再需要手动检测 iPhone X。 该框架现在包括对 iOS 安全区域插入的支持。 有关详细信息,请参阅 SafeArea https://docs.flutter.io/flutter/widgets/SafeArea-class.html类。


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看https://github.com/flutter/flutter/issues/12099#issuecomment-365037881 ,或将帖子静音https://github.com/notifications/unsubscribe-auth/AAXG50EFNdHLQGdDX2_fopnUJYUM2fLjks5tUJN5gaJpZM4PYJoo

812 是 iPhone X 的屏幕高度,以逻辑像素 (375 x 812) 为单位; 我假设检查宽度/高度的原因是为了处理两个屏幕方向。

谢谢,但是逻辑像素是如何计算的? 我想知道 812 究竟是如何得出的。

2018 年 2 月 12 日下午 12:18,Chris Bracken [email protected]写道:

812 是 iPhone X 的屏幕高度,以逻辑像素 (375 x 812) 为单位; 我假设宽度/高度来处理方向。


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看https://github.com/flutter/flutter/issues/12099#issuecomment-365048977 ,或将帖子静音https://github.com/notifications/unsubscribe-auth/AAXG566OFDa4iJofCYFYy8YLwS1B0h1Zks5tUJyhgaJpZM4PYJoo

Flutter 计算逻辑像素与设备操作系统报告的内容一致。 Apple 将 iPhone X 屏幕记录为 375 x 812 逻辑像素(请参阅屏幕尺寸下方的位)。

就 Flutter 为在 MediaQuery 中报告维度所做的计算而言,我们只是报告了底层操作系统向我们报告的内容,乘以它们向我们报告的比例因子。 iPhone X 报告逻辑分辨率为 375 x 812,比例因子为 3。 MediaQuery.of(context).size返回以逻辑像素为单位的大小(iPhone X 上为 375 x 812), MediaQuery.of(context).devicePixelRatio返回比例因子(在 iPhone X 上为 3)。

就我们如何在 iOS 上获取这些值而言,比例是通过调用[UIScreen mainScreen].scale和大小仅来自视图的self.bounds (此处为引擎代码,此处MediaQuery 代码),因此它是值得注意的是,上述检查屏幕宽度或长度是否为 812 仅在 Flutter 视图为全屏的情况下才是安全的——幸运的是,对于大多数编写 Flutter 应用程序的人来说,情况就是如此。 :)

无论颤动视图是全屏视图还是应用程序的子视图,使用MediaQuery.of(context).padding应该始终是安全的。

当我们谈论屏幕尺寸时——我发现iPhone 分辨率终极指南比 Apple 的官方文档方便得多。 在这里留下一个链接,以防其他人发现它有用。

此页面是否有帮助?
0 / 5 - 0 等级