https://youtu.be/rDrn2S6UWnk
https://www.bilibili.com/video/BV1TeyBYgE3V/
原文 Flutter 完整面试问题及答案03
本文是 flutter 面试问题的第三讲,高频问答 10 题。
在 Flutter 中,AspectRatio
小部件用于控制其子小部件的宽高比。它能够确保子小部件在不同屏幕尺寸或方向下保持指定的比例,从而使布局更加美观和一致。
主要功能
保持比例:
AspectRatio
小部件可以指定一个宽高比(例如 16:9、4:3 等),确保其子小部件在布局时遵循这个比例。自动调整大小:
AspectRatio
会自动调整子小部件的大小,以保持指定的宽高比。适应不同屏幕:
AspectRatio
非常有用,可以确保图像或视频等媒体内容在不同设备和屏幕尺寸上以正确的比例显示。使用示例
下面是一个使用 AspectRatio
的简单示例:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('AspectRatio Example')),
body: Center(
child: AspectRatio(
aspectRatio: 16 / 9, // 设置宽高比为 16:9
child: Container(
color: Colors.blue,
child: Center(
child: Text(
'16:9 Aspect Ratio',
style: TextStyle(color: Colors.white, fontSize: 24),
),
),
),
),
),
),
);
}
}
何时使用 AspectRatio
AspectRatio
可以帮助适应不同设备,确保良好的用户体验。AspectRatio
是一个非常实用的 Flutter 小部件,可以帮助开发者控制子小部件的宽高比,确保在不同屏幕尺寸和方向下保持一致的布局和美观的外观。
在 Flutter 中,从 State
中访问 StatefulWidget
的属性非常简单。StatefulWidget
的 State
类通常会有一个指向其父 StatefulWidget
的引用,可以通过 widget
属性来访问。
示例
以下是一个简单的示例,展示如何从 State
中访问 StatefulWidget
的属性:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: MyStatefulWidget(title: 'Hello, Flutter!'),
);
}
}
class MyStatefulWidget extends StatefulWidget {
final String title;
MyStatefulWidget({Key? key, required this.title}) : super(key: key);
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
// 通过 widget.title 访问 StatefulWidget 的属性
print('Current title: ${widget.title}');
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title), // 使用 widget.title
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
关键点
State
类中,可以使用 widget
属性访问 StatefulWidget
的属性。这个 widget
属性是 State
类的一个成员,指向当前的 StatefulWidget
实例。setState
更新状态时,仍然可以访问 widget
的属性,确保数据的一致性。使用场景
StatefulWidget
的属性动态更新 UI 时,可以使用这种方法。State
中访问 StatefulWidget
的属性,以便进行相应的逻辑处理。从 State
中访问 StatefulWidget
的属性是 Flutter 中常见的操作,通过 widget
属性可以轻松实现。这种方法对于构建动态和响应式的 UI 非常重要。
在 Flutter 中,Future
用于处理异步操作,允许你在代码中定义可能需要时间才能完成的任务。以下是三个常见的使用 Future
的操作示例:
使用 Future
进行网络请求是最常见的用例之一。你可以使用 http
包来发送 GET 请求并获取数据。
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(home: FetchDataExample());
}
}
class FetchDataExample extends StatefulWidget {
_FetchDataExampleState createState() => _FetchDataExampleState();
}
class _FetchDataExampleState extends State<FetchDataExample> {
Future<List<dynamic>> fetchData() async {
final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));
if (response.statusCode == 200) {
return json.decode(response.body);
} else {
throw Exception('Failed to load data');
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Fetch Data Example')),
body: FutureBuilder<List<dynamic>>(
future: fetchData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
} else {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return ListTile(title: Text(snapshot.data![index]['title']));
},
);
}
},
),
);
}
}
使用 Future.delayed
创建一个延时操作,通常用于模拟网络请求或其他耗时操作。
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(home: DelayedExample());
}
}
class DelayedExample extends StatefulWidget {
_DelayedExampleState createState() => _DelayedExampleState();
}
class _DelayedExampleState extends State<DelayedExample> {
String _message = 'Waiting...';
Future<void> _showMessage() async {
await Future.delayed(Duration(seconds: 2)); // 延迟 2 秒
setState(() {
_message = 'Hello after 2 seconds!';
});
}
void initState() {
super.initState();
_showMessage(); // 调用延时操作
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Delayed Example')),
body: Center(
child: Text(_message, style: TextStyle(fontSize: 24)),
),
);
}
}
使用 Future
读取本地文件,通常通过 dart:io
包实现。
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(home: FileReadExample());
}
}
class FileReadExample extends StatefulWidget {
_FileReadExampleState createState() => _FileReadExampleState();
}
class _FileReadExampleState extends State<FileReadExample> {
String _fileContent = 'Loading...';
Future<String> readFile() async {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/example.txt');
return file.readAsString();
}
void initState() {
super.initState();
readFile().then((content) {
setState(() {
_fileContent = content;
});
}).catchError((error) {
setState(() {
_fileContent = 'Error: $error';
});
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('File Read Example')),
body: Center(
child: Text(_fileContent, style: TextStyle(fontSize: 24)),
),
);
}
}
以上是三个常见的使用 Future
的操作示例:
http
包获取数据。Future.delayed
模拟异步操作。在 Flutter 中,SafeArea
是一个小部件,用于确保其子小部件在可视区域内展示,避免被设备的系统 UI(如状态栏、导航栏、刘海等)遮挡。它通过自动添加适当的填充(padding)来确保内容不被这些系统元素覆盖。
主要功能
自动填充:
SafeArea
会根据设备的屏幕边缘和系统 UI 的位置,自动添加适当的填充,以确保子小部件不会被遮挡。适应不同设备:
SafeArea
能够动态调整填充,以适应各种设备的特性。简化布局:
SafeArea
可以减少手动计算填充的需要,让布局更加简洁和易于维护。使用示例
下面是一个简单的示例,展示如何使用 SafeArea
:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('SafeArea Example')),
body: SafeArea(
child: Center(
child: Text(
'This text is safe from being obscured by system UI.',
style: TextStyle(fontSize: 24),
),
),
),
),
);
}
}
何时使用 SafeArea
SafeArea
是一个非常实用的小部件,能够帮助开发者确保界面元素在不同设备和屏幕尺寸下都能良好显示,避免被系统 UI 遮挡。通过使用 SafeArea
,可以使布局更加安全和易于维护。
在 Flutter 中,mainAxisSize
是一个用于控制主轴(main axis)上小部件大小的属性,通常在 Row
和 Column
小部件中使用。它决定了子小部件在主轴方向上应该占用的空间。mainAxisSize
有两个值:
MainAxisSize.max
(默认值):子小部件在主轴上尽可能扩展,填满可用空间。MainAxisSize.min
:子小部件在主轴上只占用其内容所需的最小空间。何时使用 mainAxisSize
控制布局行为:
MainAxisSize.max
。这是默认行为,适用于大多数情况。MainAxisSize.min
。在动态布局中:
mainAxisSize.min
可以帮助你控制小部件的大小。结合其他布局小部件:
Expanded
或 Flexible
)结合使用时,mainAxisSize
可以帮助更好地控制空间分配。示例
MainAxisSize.max
(默认行为)import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('MainAxisSize Example')),
body: Column(
mainAxisSize: MainAxisSize.max, // 默认值
children: <Widget>[
Container(color: Colors.red, height: 100),
Container(color: Colors.green, height: 100),
],
),
),
);
}
}
MainAxisSize.min
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('MainAxisSize.min Example')),
body: Column(
mainAxisSize: MainAxisSize.min, // 只占用子小部件所需的空间
children: <Widget>[
Container(color: Colors.red, height: 100),
Container(color: Colors.green, height: 100),
],
),
),
);
}
}
使用 mainAxisSize
可以帮助你更好地控制 Row
和 Column
中子小部件在主轴上的空间占用。根据布局需求选择合适的 mainAxisSize
值,可以使你的 Flutter 应用布局更加灵活和美观。
在 Flutter 中,SizedBox
和 Container
都是常用的布局小部件,但它们之间存在一些关键的区别和适用场景。以下是对它们的比较:
基本功能
SizedBox:
Container:
性能
SizedBox:
SizedBox
具有更好的性能,因为它只处理尺寸,不会涉及额外的绘制和布局逻辑。Container:
Container
可能会引入额外的开销,尤其是当你只需要设置尺寸时。使用场景
SizedBox:
SizedBox
更为简洁和高效。Container:
Container
更为合适。示例
SizedBox 示例:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('SizedBox Example')),
body: Center(
child: SizedBox(
width: 100,
height: 100,
child: Container(color: Colors.blue), // 控制子小部件的尺寸
),
),
),
);
}
}
Container 示例:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Container Example')),
body: Center(
child: Container(
width: 100,
height: 100,
color: Colors.blue,
margin: EdgeInsets.all(10), // 设置边距
padding: EdgeInsets.all(20), // 设置内边距
child: Text('Hello', style: TextStyle(color: Colors.white)), // 子小部件
),
),
),
);
}
}
SizedBox
时选择简单、性能优越的尺寸控制。Container
时选择更复杂的布局和样式需求。根据具体的需求选择适合的小部件,可以提高代码的可读性和性能。
在 Flutter 中,Visibility
、Opacity
和 Offstage
都是用于控制小部件在界面上的显示和隐藏的组件,但它们的使用场景和效果各有不同。以下是对这三个组件的使用场景的详细说明:
Visibility
是合适的。示例:
Visibility(
visible: isVisible, // 控制可见性
child: Text('This text is conditionally visible.'),
)
Opacity
来调整其透明度。示例:
Opacity(
opacity: 0.5, // 设置透明度
child: Text('This text is semi-transparent.'),
)
Visibility
不同,Offstage
会在布局中完全忽略被隐藏的小部件。Offstage
可以提高性能,因为它不会参与布局计算。示例:
Offstage(
offstage: !isVisible, // 控制是否在屏幕外
child: Text('This text is offstage.'),
)
根据具体的需求选择合适的组件,可以使你的 Flutter 应用具有更好的用户体验和性能。
不可以
在 Flutter 中,Container
小部件的 color
和 decoration
属性不能同时使用。具体来说,如果你同时为这两个属性赋值,decoration
属性将优先于 color
属性。也就是说,color
属性会被忽略。
解释
color
属性:
decoration
属性:
decoration
,color
属性的值会被忽略。示例
以下是一个示例,展示同时使用 color
和 decoration
的效果:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Container Example')),
body: Center(
child: Container(
color: Colors.red, // 这个属性会被忽略
decoration: BoxDecoration(
color: Colors.blue, // 这个属性会生效
border: Border.all(color: Colors.black, width: 2),
),
width: 100,
height: 100,
),
),
),
);
}
}
在这个示例中,尽管 color
属性被设置为红色,但最终容器的背景是蓝色,因为 decoration
属性中的 color
会覆盖它。
如果你需要同时设置颜色和其他装饰效果,应该使用 decoration
属性,并在其中设置 color
。如果只需要简单的背景色,可以直接使用 color
属性。确保在使用时选择适合的属性,以避免不必要的混淆。
在 Flutter 中,为了使 CrossAxisAlignment.baseline
正常工作,除了设置 CrossAxisAlignment.baseline
外,你还需要确保设置 textBaseline
属性。这个属性通常在 Row
或 Column
小部件中使用。
具体要求
设置 textBaseline
:
CrossAxisAlignment.baseline
时,你必须指定 textBaseline
,以告诉 Flutter 如何计算基线。可以选择的基线有:
TextBaseline.alphabetic
:用于大多数文本。TextBaseline.ideographic
:用于某些语言的文本。子小部件必须是文本:
Row
或 Column
中的子小部件能够提供基线。这通常是文本小部件(如 Text
)或其他可以确定基线的小部件。示例
下面是一个示例,展示如何使用 CrossAxisAlignment.baseline
:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('CrossAxisAlignment.baseline Example')),
body: Center(
child: Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic, // 确保设置 textBaseline
children: <Widget>[
Text(
'Hello',
style: TextStyle(fontSize: 40),
),
Text(
'World',
style: TextStyle(fontSize: 20),
),
],
),
),
),
);
}
}
要使 CrossAxisAlignment.baseline
工作,你必须:
textBaseline
属性,以指定计算文本基线的方式。在 Flutter 中,resizeToAvoidBottomInset
是 Scaffold
的一个属性,用于控制当键盘弹出时,是否调整界面的大小以避免被键盘遮挡。这个属性的使用场景主要包括:
使用场景
输入框在底部:
TextField
)并且这些输入框位于屏幕底部时,设置 resizeToAvoidBottomInset: true
可以确保键盘弹出时,输入框不会被遮挡。聊天应用:
表单:
resizeToAvoidBottomInset
可以改善用户体验。动态布局:
示例
以下是一个简单的示例,展示如何使用 resizeToAvoidBottomInset
:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('resizeToAvoidBottomInset Example')),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(labelText: 'Enter your message'),
),
SizedBox(height: 600), // 模拟其他内容
],
),
),
),
resizeToAvoidBottomInset: true, // 确保键盘弹出时调整大小
),
);
}
}
注意事项
SingleChildScrollView
或 ListView
等可滚动的小部件),则可以选择不使用该属性。使用 resizeToAvoidBottomInset
是为了确保在键盘弹出时,用户可以方便地看到和输入内容。适用于包含输入框的场景,特别是在聊天、表单等应用中,能够显著提升用户体验。
感谢阅读本文
如果有什么建议,请在评论中让我知道。我很乐意改进。
© 猫哥
ducafecat.com
end