我正在尝试构建一个简单的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,
),
),
),
],
),
);
}
}
结果图像如下:
当我向应用程序中添加AppBar
或BottomNavigationBar
时,事情开始变得棘手。这两个小部件的默认高度均为 56 像素。给定iPhone SE上devicePixelRatio
的{{1}}为2
,我需要将背景图像的大小裁剪为750 x 1110
,以使覆盖仍然准确(1334 - 2 * 56 (AppBar) - 2 * 56 (BottomNavigationBar)
)。 / p>
对于其他设备(例如iPhone XR),事情变得更加复杂,在这些地方还必须考虑安全区域的大小。对于Android,还有更多不同的屏幕分辨率可用。
我现在的问题是:而不是为20-30种以上的不同屏幕尺寸创建不同大小的背景图像-Flutter中有没有更有效的方式在非常特定的屏幕上绘制诸如圆形Container
之类的小部件位置与屏幕实际尺寸无关吗?
答案 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图片:
示例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")),
],
),
);
}
}
答案 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,
),
),
),
],
),
)