针对不同屏幕尺寸在特定像素位置绘制小部件

Posted

技术标签:

【中文标题】针对不同屏幕尺寸在特定像素位置绘制小部件【英文标题】:Drawing Widgets at specific pixel locations for different screen sizes 【发布时间】:2020-11-20 09:06:16 【问题描述】:

我正在尝试构建一个简单的 Flutter 应用程序,它显示全屏背景图像并允许用户将某些小部件(即基本圆圈)从预定义的起始位置(以像素为单位)拖动到预定义的目标位置(也以像素为单位)。 TouchSurgery 应用程序的以下屏幕截图显示了与我尝试实现的设置非常相似的设置(绿色圆圈 = 起始位置,白色圆圈 = 目标位置):

此时我最担心的是不同的屏幕尺寸。假设我们有一台分辨率为750 x 1334 的iPhone SE(第二代)。我可以创建以下具有所需分辨率的背景图像,并随机确定所需的起始位置为坐标(430, 949)(为简单起见,我们可以忽略目标位置):

使用以下小部件,我可以在起点顶部渲染一个圆形Container

class MyHomePage extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    var dpr = MediaQuery.of(context).devicePixelRatio;
    return Scaffold(
      body: Stack(
        children: <Widget>[
          Container(
            decoration: BoxDecoration(
              image: DecorationImage(
                image: AssetImage("assets/iPhoneSE.png"),
                fit: BoxFit.fill,
              ),
            ),
          ),
          Positioned(
            left: 430 / dpr,
            top: 949 / dpr,
            child: Container(
              width: 77.0 / dpr,
              height: 77.0 / dpr,
              decoration: BoxDecoration(
                shape: BoxShape.circle,
                color: Colors.red,
              ),
            ),
          ),
        ],
      ),
    );
  

生成的图像如下所示:

当我将AppBarBottomNavigationBar 添加到我的应用程序时,事情开始变得棘手。两个小部件的默认高度均为 56 像素。鉴于 iPhone SE 上的 devicePixelRatio2,我需要将背景图像的大小裁剪为 750 x 1110 以使叠加层仍然准确 (1334 - 2 * 56 (AppBar) - 2 * 56 (BottomNavigationBar))。

对于 iPhone XR 等其他设备,情况变得更加复杂,还必须考虑安全区域的大小。对于 android,还有更多不同的屏幕分辨率可供选择。

我现在的问题如下:不是为 20-30+ 不同的屏幕尺寸创建不同尺寸的背景图像,而是在 Flutter 中有更有效的方法在非常特定的屏幕位置绘制小部件,例如圆形 Container独立于实际屏幕尺寸工作?

【问题讨论】:

能不能把图片最小化,问题看很久了 你想在图像的特定位置画一个圆圈,对吧? IE。圆圈相对于图像的位置很重要,而不是相对于屏幕 正确,但是图像当然应该被绘制为背景图像,所以这个应该与屏幕尺寸相匹配 【参考方案1】:

您需要在定位您的 Positioned Widget 之前获取图像容器的大小。

因为正如您所说,屏幕尺寸可能会发生变化,而与图像尺寸无关(例如,屏幕更高但具有更大的 SafeArea,或者有一个 appBar 和 BottomAppBar。即使屏幕尺寸,图像也可能是相同的尺寸增加...)

由于您的 Positioned 小部件和您的图像容器在相同的构建方法中,您必须使用 LayoutBuilder 小部件来跟踪图像容器的大小,然后再继续构建您的定位小部件。

方法如下: (我已经包含了 2 个完整的工作示例,以便您可以看到红色圆圈与背景图像保持相同的相对位置,即使图像大小发生变化。您更正的代码是第一个示例)。

示例 1


/*
  I used these calculated ratios based on your code. 
  Feel free to use any other way to get these ratios. 
  The method will be the same.

  - The image takes all the available width and height 

  - The positioned element is postioned : 
      58.9% from the left of the image container  
      72% from the top of the image container

  - The inner container's:
      width is 7.129629629% of the Image Container's width,
      height is 4.292084726% of the Image Container's height, 
*/

import 'package:flutter/material.dart';

void main() 
  runApp(MyApp());


class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      home: MyHomePage(),
    );
  


class MyHomePage extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return LayoutBuilder(builder: (context, constraints)  //This is key 
      return Scaffold(
        body: Stack(
          children: <Widget>[
            Container(
              decoration: BoxDecoration(
                image: DecorationImage(
                  image: AssetImage("assets/iPhoneSE.png"),
                  fit: BoxFit.fill,
                ),
              ),
            ),
            Positioned(
              left: 0.589 * constraints.maxWidth,
              top: 0.72 * constraints.maxHeight,
              child: Container(
                width: 0.07129629629 * constraints.maxWidth,
                height: 04292084726 * constraints.maxHeight,
                decoration: BoxDecoration(
                  shape: BoxShape.circle,
                  color: Colors.red,
                ),
              ),
            ),
          ],
        ),
      );
    );
  


示例 1 图片:

示例 2(带有 AppBar 和 BottomAppBar)

import 'package:flutter/material.dart';

void main() 
  runApp(MyApp());


class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      home: MyHomePage(),
    );
  


class MyHomePage extends StatefulWidget 
  @override
  _MyHomePageState createState() => _MyHomePageState();


class _MyHomePageState extends State<MyHomePage> 
  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(
        title: Text("Title of app"),
      ),
      body: LayoutBuilder(builder: (context, constraints) 
        return Column(
          children: <Widget>[
            Flexible(
              fit: FlexFit.loose,
              child: Stack(
                children: <Widget>[
                  Container(
                    decoration: BoxDecoration(
                      image: DecorationImage(
                        image: AssetImage("assets/iPhoneSE.png"),
                        fit: BoxFit.fill,
                      ),
                    ),
                  ),
                  Positioned(
                    left: 0.589 * constraints.maxWidth,
                    top: 0.72 * constraints.maxHeight,
                    child: Container(
                      width: 0.07129629629 * constraints.maxWidth,
                      height: 0.04292084726 * constraints.maxHeight,
                      decoration: BoxDecoration(
                        shape: BoxShape.circle,
                        color: Colors.red,
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ],
        );
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(
              icon: Icon(
                Icons.home,
              ),
              title: Text("Home")),
          BottomNavigationBarItem(
              icon: Icon(Icons.account_circle), title: Text("Profile")),
        ],
      ),
    );
  


示例 2 图片:

【讨论】:

【参考方案2】:

如何在您的堆栈上使用 Transform.scale 小部件并调整整个大小以适应设备的任何限制?

类似这样的:

Transform.scale(
  alignment: Alignment.topLeft,
  scale: scaleVar,
  child: Stack(
    children: <Widget>[
      Positioned(
        top: 0,
        left: 0,
        child: Image(
          image: AssetImage("assets/iPhoneSE.png"),
          alignment: Alignment.topLeft,
        ),
      ),
      Positioned(
        left: 430,
        top: 949,
        child: Container(
          width: 77.0,
          height: 77.0,
          decoration: BoxDecoration(
            shape: BoxShape.circle,
            color: Colors.red,
          ),
        ),
      ),
    ],
  ),
 )

【讨论】:

以上是关于针对不同屏幕尺寸在特定像素位置绘制小部件的主要内容,如果未能解决你的问题,请参考以下文章

如何使我的自定义 HTML 小部件针对不同的屏幕尺寸显示不同?

在模拟器中或根据不同的屏幕尺寸将小部件定位在堆栈中的位置变化。扑

各种屏幕的标准小部件尺寸

在 Android 上针对不同的屏幕尺寸和密度重用可绘制图像

在卡片视图中排列小部件

适用于不同屏幕尺寸的Android布局