适用于不同屏幕尺寸的特定像素位置的绘图小部件

时间:2020-07-30 15:15:23

标签: flutter flutter-layout

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

enter image description here

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

enter image description here

使用以下小部件,我可以在起点上方绘制圆形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,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

结果图像如下:

enter image description here

当我向应用程序中添加AppBarBottomNavigationBar时,事情开始变得棘手。这两个小部件的默认高度均为 56 像素。给定iPhone SE上devicePixelRatio的{​​{1}}为2,我需要将背景图像的大小裁剪为750 x 1110,以使覆盖仍然准确(1334 - 2 * 56 (AppBar) - 2 * 56 (BottomNavigationBar))。 / p>

对于其他设备(例如iPhone XR),事情变得更加复杂,在这些地方还必须考虑安全区域的大小。对于Android,还有更多不同的屏幕分辨率可用。

我现在的问题是:而不是为20-30种以上的不同屏幕尺寸创建不同大小的背景图像-Flutter中有没有更有效的方式在非常特定的屏幕上绘制诸如圆形Container之类的小部件位置与屏幕实际尺寸无关吗?

2 个答案:

答案 0 :(得分:4)

在放置Positioned Widget之前,您需要获取图像容器的大小。

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

由于“定位”小部件和图像容器采用相同的构建方法,因此在继续构建“定位”小部件之前,必须使用LayoutBuilder小部件来跟踪图像容器的大小。

方法如下:
(我提供了两个可以正常工作的示例,以便您可以看到红色圆圈相对于背景图像保持相同的相对位置,即使图像大小发生变化也是如此。更正的代码是第一个示例。)

示例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图片:

enter image description here

示例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图片:
enter image description here

答案 1 :(得分:1)

如何在您的堆栈上使用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,
          ),
        ),
      ),
    ],
  ),
 )