https://www.bilibili.com/video/BV1zqynY5E1g/
https://youtu.be/GztdZKomCDs
原文 Flutter 完整面试问题及答案04
本文是 flutter 面试问题的第四讲,高频问答 10 题。
在 Flutter(以及 Dart)中,as
、show
和 hide
是用于 import
语句的关键字,帮助开发者管理命名空间和导入的符号。下面是它们的区别和使用场景:
as
as
为导入的库指定一个别名。示例:
import 'package:flutter/material.dart' as myMaterial;
void main() {
runApp(myMaterial.MaterialApp(
home: myMaterial.Scaffold(
appBar: myMaterial.AppBar(title: myMaterial.Text('Hello')),
body: myMaterial.Center(child: myMaterial.Text('Hello World')),
),
));
}
show
show
可以减少命名空间的混乱并提高代码的可读性。示例:
import 'package:flutter/material.dart' show Text, Scaffold;
void main() {
runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Hello')),
body: Center(child: Text('Hello World')),
),
));
}
hide
hide
。示例:
import 'package:flutter/material.dart' hide Text;
void main() {
runApp(MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Hello')), // 使用 Text 类会报错,因为它被隐藏
body: Center(child: Text('Hello World')), // 这会报错
),
));
}
as
:为导入的库设置别名,避免命名冲突。show
:仅导入库中的特定符号,提高代码的清晰度。hide
:导入库中的所有符号,但排除特定符号,控制命名空间。根据具体的需求选择适合的导入方式,可以使代码更加整洁、可读和易于维护。
在 Flutter 中,TextEditingController
是一个用于管理文本输入的控制器。它主要用于处理 TextField
或 TextFormField
的文本输入,提供了一种方式来读取、修改和清空文本内容。以下是 TextEditingController
的主要作用和使用场景:
主要作用
获取文本内容:
TextEditingController
,你可以方便地获取用户在 TextField
中输入的文本内容。更新文本内容:
TextEditingController
来动态更新 TextField
中的文本。例如,可以在某些事件发生时(如按钮点击)更新文本。监听文本变化:
TextEditingController
,你可以添加监听器来监控文本的变化,适用于需要实时处理输入的场景。清空文本:
TextField
中的文本内容。使用场景
示例
以下是一个简单的示例,展示如何使用 TextEditingController
:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final TextEditingController _controller = TextEditingController();
void _showInput() {
final inputText = _controller.text; // 获取输入文本
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text('You entered: $inputText'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('OK'),
),
],
);
},
);
}
void dispose() {
_controller.dispose(); // 释放控制器资源
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('TextEditingController Example')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _controller, // 设置控制器
decoration: InputDecoration(labelText: 'Enter something'),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _showInput,
child: Text('Show Input'),
),
],
),
),
);
}
}
TextEditingController
是在 Flutter 中处理文本输入的强大工具,能够帮助你获取、更新、监听和清空文本内容。它在表单、搜索框和动态输入等场景中非常实用。确保在不再需要控制器时调用 dispose()
方法来释放资源。
在 Flutter 中,使用 ListView
的 reverse
属性可以让列表的滚动方向反转,即从底部开始向顶部滚动。这在某些特定的场景中非常有用,以下是一些常见的使用场景及其原因:
使用场景
聊天应用:
reverse: true
可以使得列表从底部开始滚动,以便用户能够直接看到最新的消息。时间线或动态列表:
reverse
属性可以实现这一效果。增强用户体验:
示例
以下是一个使用 reverse
属性的简单示例:
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('ListView Reverse Example')),
body: ListView.builder(
reverse: true, // 反转列表
itemCount: 20,
itemBuilder: (context, index) {
return ListTile(
title: Text('Item ${index + 1}'),
);
},
),
),
);
}
}
使用 ListView
的 reverse
属性可以在需要从底部开始显示内容的场景中提高用户体验,尤其是在聊天应用和动态列表中。这种反向滚动的布局方式使得用户可以更方便地查看最新信息,而无需频繁滚动。
在 Flutter 中,模态对话框(Modal Dialog) 和 持久底部抽屉(Persistent Bottom Sheet) 是两种不同的用户界面元素,它们在使用场景、外观和交互方式上有明显的区别。以下是它们的主要区别以及示例说明。
主要区别
模态对话框(Modal Dialog):
持久底部抽屉(Persistent Bottom Sheet):
示例
模态对话框示例
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('Modal Dialog Example')),
body: Center(
child: ElevatedButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Confirm Action'),
content: Text('Are you sure you want to proceed?'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(); // 关闭对话框
},
child: Text('Cancel'),
),
TextButton(
onPressed: () {
// 执行确认操作
Navigator.of(context).pop();
},
child: Text('OK'),
),
],
);
},
);
},
child: Text('Show Modal Dialog'),
),
),
),
);
}
}
持久底部抽屉示例
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('Persistent Bottom Sheet Example')),
body: Center(
child: ElevatedButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (context) {
return Container(
height: 200,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Persistent Bottom Sheet'),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(); // 关闭底部抽屉
},
child: Text('Close'),
),
],
),
),
);
},
);
},
child: Text('Show Bottom Sheet'),
),
),
),
);
}
}
根据具体的用户交互需求选择合适的组件,可以提升应用的用户体验和可用性。
在 Flutter 中,InheritedWidget 和 Provider 都是用于在小部件树中传递数据的机制,但它们在使用方式、复杂性和功能上存在一些重要的区别。以下是它们的主要不同点:
定义与目的
InheritedWidget:
Provider:
provider
),基于 InheritedWidget
构建的更高级别的状态管理解决方案。使用复杂性
InheritedWidget:
InheritedWidget
类,管理状态并在状态变化时调用 notifyListeners()
。Provider:
InheritedWidget
,可以直接使用 ChangeNotifier
和 ChangeNotifierProvider
来管理状态。性能
InheritedWidget:
Provider:
示例
使用 InheritedWidget 示例
import 'package:flutter/material.dart';
class MyInheritedWidget extends InheritedWidget {
final int data;
MyInheritedWidget({Key? key, required this.data, required Widget child})
: super(key: key, child: child);
static MyInheritedWidget? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
}
bool updateShouldNotify(MyInheritedWidget oldWidget) {
return oldWidget.data != data;
}
}
class MyHomePage extends StatelessWidget {
Widget build(BuildContext context) {
return MyInheritedWidget(
data: 42,
child: Scaffold(
appBar: AppBar(title: Text('InheritedWidget Example')),
body: Center(
child: Text('Data: ${MyInheritedWidget.of(context)!.data}'),
),
),
);
}
}
void main() {
runApp(MaterialApp(home: MyHomePage()));
}
使用 Provider 示例
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class MyModel with ChangeNotifier {
int _data = 42;
int get data => _data;
void updateData(int newData) {
_data = newData;
notifyListeners(); // 通知依赖的子小部件重建
}
}
class MyHomePage extends StatelessWidget {
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => MyModel(),
child: Scaffold(
appBar: AppBar(title: Text('Provider Example')),
body: Center(
child: Consumer<MyModel>(
builder: (context, model, child) {
return Text('Data: ${model.data}');
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read<MyModel>().updateData(100); // 更新数据
},
child: Icon(Icons.update),
),
),
);
}
}
void main() {
runApp(MaterialApp(home: MyHomePage()));
}
InheritedWidget
,提供更高级和易用的 API,适合中大型应用中的状态管理。选择使用哪种方式,通常取决于你的应用复杂度和状态管理的需求。对于大多数应用来说,Provider
是推荐的选择,因为它能够简化状态管理并提高代码的可读性。
在 Flutter 中,UnmodifiableListView
是一个类,提供了一种不可修改的列表视图。它是 dart:collection
库的一部分,用于封装一个可变的 List
,并确保该列表在创建后无法更改。这意味着你不能添加、删除或修改列表中的元素。
主要特点
不可修改:
UnmodifiableListView
中的元素不能被修改。任何尝试修改操作(如添加、删除或更新元素)都会抛出异常。适用于暴露内部状态:
UnmodifiableListView
。这提供了一种安全的方式来共享数据。视图的同步:
UnmodifiableListView
是对底层可变列表的视图。如果底层列表发生变化,UnmodifiableListView
也会反映这些变化,但外部不能改变它。使用场景
UnmodifiableListView
。示例
以下是一个简单的示例,展示如何使用 UnmodifiableListView
:
import 'package:flutter/material.dart';
import 'dart:collection';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final List<String> _items = ['Apple', 'Banana', 'Cherry'];
Widget build(BuildContext context) {
// 使用 UnmodifiableListView 包装可变列表
final unmodifiableList = UnmodifiableListView(_items);
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('UnmodifiableListView Example')),
body: ListView.builder(
itemCount: unmodifiableList.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(unmodifiableList[index]),
);
},
),
),
);
}
}
注意事项
UnmodifiableListView
时,确保底层列表在需要的情况下保持不变,或者在必要时使用其他方式来管理列表的状态。List
,而不是 UnmodifiableListView
。UnmodifiableListView
提供了一种安全的方式来共享列表数据,防止外部代码对列表进行修改。这在需要保护内部状态的场景中特别有用。
在 Flutter(以及 Dart)中,??
和 ?.
是两种不同的运算符,它们在处理 null 值时的行为有所不同。以下是它们的主要区别和使用场景:
1. ??
运算符
??
是 null 合并运算符,用于提供一个默认值。当左侧的表达式为 null
时,返回右侧的值;否则,返回左侧的值。null
的值,并为其提供一个后备值。示例:
void main() {
String? name;
String displayName = name ?? 'Guest'; // name 为 null,返回 'Guest'
print(displayName); // 输出: Guest
name = 'Alice';
displayName = name ?? 'Guest'; // name 不为 null,返回 'Alice'
print(displayName); // 输出: Alice
}
2. ?.
运算符
?.
是条件访问运算符,也称为 null 安全访问运算符。用于在访问对象属性或方法时安全地处理 null
值。如果左侧的对象为 null
,整个表达式将返回 null
,而不是抛出异常。null
对象的属性或方法而引发的运行时错误。示例:
class User {
String? name;
User(this.name);
}
void main() {
User? user;
print(user?.name); // user 为 null,返回 null
user = User('Alice');
print(user?.name); // user 不为 null,返回 'Alice'
}
??
运算符:
null
时返回右侧值。?.
运算符:
null
,则整个表达式返回 null
,避免运行时错误。这两个运算符在处理可能的 null 值时非常有用,提高了代码的安全性和可读性。
在 Flutter 中,ModalRoute.of(context)
是一个用于获取当前模态路由的静态方法。它的主要目的是在小部件树中查找与给定 BuildContext
相关联的 ModalRoute
实例。这个方法在处理模态对话框、底部抽屉等需要路由管理的场景中非常有用。
主要用途
获取当前路由信息:
ModalRoute
提供了关于当前路由的信息,例如路由的名称、路由的参数等。通过 ModalRoute.of(context)
,你可以方便地访问这些信息。控制路由状态:
ModalRoute
实例来控制路由的状态,例如使用 Navigator
来返回上一个路由或关闭当前模态对话框。在小部件中使用路由相关功能:
ModalRoute.of(context)
来实现。示例
以下是一个简单的示例,展示如何使用 ModalRoute.of(context)
:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: FirstPage(),
);
}
}
class FirstPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('First Page')),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondPage()),
);
},
child: Text('Go to Second Page'),
),
),
);
}
}
class SecondPage extends StatelessWidget {
Widget build(BuildContext context) {
// 获取当前的 ModalRoute
final route = ModalRoute.of(context);
return Scaffold(
appBar: AppBar(title: Text('Second Page')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Current Route: ${route?.settings.name ?? 'Unnamed'}'),
ElevatedButton(
onPressed: () {
Navigator.pop(context); // 返回上一页
},
child: Text('Go Back'),
),
],
),
),
);
}
}
ModalRoute.of(context)
的主要目的是提供对当前模态路由的访问,使得在小部件中能够获取路由信息、控制路由状态以及处理与路由相关的功能。它在构建需要路由管理的应用时非常有用,尤其是在处理对话框和底部抽屉等场景中。
在 Flutter 中,Navigator.pushNamed
和 Navigator.pushReplacementNamed
都是用于导航到新页面的方法,但它们在路由栈的管理上有显著的区别。以下是这两者的主要区别:
Navigator.pushNamed
示例:
Navigator.pushNamed(context, '/detail');
Navigator.pushReplacementNamed
示例:
Navigator.pushReplacementNamed(context, '/home');
Navigator.pushNamed
:
Navigator.pushReplacementNamed
:
根据具体的导航需求选择适合的方法,可以提高应用的用户体验和逻辑清晰度。
在 Flutter 和 Dart 中,单实例(Singleton)与作用域实例(Scoped Instance)是两种不同的对象管理和创建方式,它们在生命周期、可访问性和使用场景上有显著的区别。以下是它们的主要区别:
1. 单实例(Singleton)
示例:
class Singleton {
// 私有构造函数
Singleton._privateConstructor();
// 唯一实例
static final Singleton _instance = Singleton._privateConstructor();
// 获取实例的方法
static Singleton get instance => _instance;
// 示例方法
void someMethod() {
print("This is a singleton method.");
}
}
// 使用
void main() {
var singleton1 = Singleton.instance;
var singleton2 = Singleton.instance;
print(identical(singleton1, singleton2)); // 输出: true
}
2. 作用域实例(Scoped Instance)
示例(使用 Provider 作为作用域实例):
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// 状态类
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Counter(), // 创建作用域实例
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: CounterPage(),
);
}
}
class CounterPage extends StatelessWidget {
Widget build(BuildContext context) {
final counter = Provider.of<Counter>(context); // 访问作用域实例
return Scaffold(
appBar: AppBar(title: Text('Scoped Instance Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Count: ${counter.count}'),
ElevatedButton(
onPressed: counter.increment,
child: Text('Increment'),
),
],
),
),
);
}
}
单实例(Singleton):
作用域实例(Scoped Instance):
根据你的应用需求和架构选择合适的实例管理方式,可以提高代码的可维护性和清晰度。
感谢阅读本文
如果有什么建议,请在评论中让我知道。我很乐意改进。
© 猫哥
ducafecat.com
end