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

Sch*_*ges 4 flutter flutter-layout

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

在此输入图像描述

我目前最关心的是不同的屏幕尺寸。假设我们有一台 iPhone SE(第二代),分辨率为750 x 1334. 我可以使用所需的分辨率创建以下背景图像,并随机确定所需的起始位置为坐标(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,
              ),
            ),
          ),
        ],
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

结果图像如下所示:

在此输入图像描述

当我在应用程序中添加一个AppBar或一个时,事情开始变得棘手。BottomNavigationBar两个 Widget 的默认高度均为56像素。考虑到devicePixelRatioiPhone 2SE 上的 ,我需要裁剪背景图像的大小,以750 x 1110使叠加层仍然准确 ( 1334 - 2 * 56 (AppBar) - 2 * 56 (BottomNavigationBar))。

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

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

len*_*enz 7

在定位定位小部件之前,您需要获取图像容器的大小。

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

由于您的定位小部件和图像容器采用相同的构建方法,因此您必须使用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,
                ),
              ),
            ),
          ],
        ),
      );
    });
  }
}

Run Code Online (Sandbox Code Playgroud)

示例 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")),
        ],
      ),
    );
  }
}

Run Code Online (Sandbox Code Playgroud)

示例 2 图片:
在此输入图像描述