本文是关于 Flutter 面试问题的第三篇,以往文章: - https://ducafecat.com/blog/flutter-interview-questions-with-answers-01 - https://ducafecat.com/blog/flutter-interview-questions-with-answers-02 如果你想系统学习请关注猫哥课程 https://ducafecat.com 。

Flutter 面试题整理 03

视频

前言

原文 https://ducafecat.com/blog/flutter-interview-questions-with-answers-03

本文是关于 Flutter 面试问题的第三篇,以往文章:

如果你想系统学习请关注猫哥课程 https://ducafecat.com

正文

21 Flutter 中的 Navigator 是什么?

在Flutter中,Navigator是一个用于管理路由(页面)导航的类。它提供了一组方法,用于在应用程序中进行页面的推入(push)、弹出(pop)和替换(replace)操作,以及管理页面堆栈。

Navigator类是由Flutter框架提供的,可以在应用程序中使用它来管理应用程序的导航栈。导航栈是一个存储页面路由的堆栈数据结构,可以在其中推入(push)和弹出(pop)页面。

使用Navigator类,您可以执行以下操作:

  1. 推入页面(Push):将新页面推入导航栈,并显示在当前页面之上。
  2. 弹出页面(Pop):从导航栈中移除当前页面,并返回到上一个页面。
  3. 替换页面(Replace):替换当前页面为新页面,同时移除导航栈中的上一个页面。
  4. 弹出到指定页面(Pop Until):从导航栈中连续弹出页面,直到指定页面为止。
  5. 获取当前页面的上下文(Context):通过Navigator类的方法获取当前页面的上下文,以便执行其他操作。

通过使用Navigator类,您可以实现应用程序中的页面导航和页面间的切换,例如在用户进行操作时推入新页面、返回上一个页面或者在特定条件下替换当前页面。

在Flutter中,通常将Navigator放置在应用程序的根部,以便在整个应用程序中进行页面导航的管理。

使用例子:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Go to Details'),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => DetailsPage()),
            );
          },
        ),
      ),
    );
  }
}

class DetailsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Details'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Go Back'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ),
    );
  }
}

22 Flutter 中的 Navigator 2.0 是什么?

在Flutter中,Navigator 2.0 是一种新的导航机制,引入了路由管理器和路由信息的概念,以提供更灵活和可扩展的页面导航功能。Navigator 2.0 是对之前的 Navigator 的改进和升级。

Navigator 2.0 的核心思想是将导航状态(包括当前页面和页面历史记录)存储在应用程序的状态中,而不是直接存储在导航栈中。它通过使用路由管理器(RouteManager)和路由信息(RouteInformation)来管理导航状态。

路由管理器(RouteManager)是一个负责管理路由的对象,它可以监听导航状态的变化,并根据路由信息构建和切换页面。它可以根据应用程序的状态、用户操作或其他条件来处理页面的推入、弹出和替换。

路由信息(RouteInformation)是一个包含导航状态的对象,它描述了当前页面的信息以及页面历史记录。它可以包含路由名称、路径、查询参数等信息,以便在导航过程中进行路由匹配和页面构建。

通过使用 Navigator 2.0,开发者可以更加灵活地管理应用程序的导航状态,并实现更复杂的导航场景,如深链接、动画过渡、持久化导航状态等。

要使用 Navigator 2.0,您需要使用 Flutter 提供的RouterRouterDelegate类来设置路由管理器,并实现自定义的路由信息。

Navigator 2.0 对于大型应用程序或需要更高级导航功能的应用程序来说是一个强大的工具,它提供了更多的灵活性和可扩展性,以满足不同的应用程序需求。

我2021年有视频说这个,有兴趣可以阅读。

23 Flutter 中的 Navigator 接收返回值

在 Flutter 中,可以通过 Navigator.push 方法的返回值来接收从目标页面返回的数据。以下是一个示例:

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Go to Details'),
          onPressed: () async {
            final result = await Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => DetailsPage()),
            );

            // 处理从 DetailsPage 返回的数据
            if (result != null) {
              // 根据返回的数据执行相应操作
              print('Received result: $result');
            }
          },
        ),
      ),
    );
  }
}

class DetailsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Details'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Go Back with Result'),
          onPressed: () {
            // 返回数据到上一个页面
            Navigator.pop(context, 'Hello from DetailsPage');
          },
        ),
      ),
    );
  }
}

在上面的示例中,HomePage 包含一个按钮,当按钮被点击时,它通过 Navigator.push 方法将用户导航到 DetailsPage。注意到 Navigator.push 方法是一个异步方法,并且它返回一个 Future 对象。

DetailsPage 中,当按钮被点击时,我们使用 Navigator.pop 方法返回数据到上一个页面。在这个例子中,我们返回了一个字符串 'Hello from DetailsPage'

HomePage 中,我们使用 await 关键字将 Navigator.push 方法的返回值赋给 result 变量。这样,当从 DetailsPage 返回时,我们可以检查 result 的值来获取返回的数据。

在示例中,我们通过检查 result 是否为 null 来确定是否收到了返回的数据。如果 result 不为 null,我们可以根据返回的数据执行相应的操作,例如打印返回的结果。

通过这种方式,您可以在 Flutter 中使用 Navigator 接收从目标页面返回的数据,并对返回的数据进行处理。

24 Flutter 中的 Navigator 嵌套使用

在Flutter中,可以通过嵌套使用Navigator来实现多级页面导航。每个嵌套的Navigator可以管理自己的页面堆栈,使得在不同层级之间进行导航变得更加灵活。

有兴趣可以阅读猫哥的文章和视频:

25 Flutter 中有哪些状态管理,你主要用哪个

在Flutter中,有多种用于状态管理的方法,每种方法都有不同的工作原理和适用场景。以下是一些常见的状态管理方法:

  1. 本地状态管理(Local State Management):本地状态管理是指在小规模应用或组件内部管理状态的简单方法。通常使用StatefulWidgetsetState来更新状态。该方法适用于较简单的应用或组件,状态的范围有限且不需要在多个组件之间共享。
  2. InheritedWidget 和 InheritedModelInheritedWidgetInheritedModel是Flutter中的两个基础类,用于在组件树中共享状态。它们通过将状态作为不可变对象传递给子组件来实现状态共享。当共享的状态发生变化时,它们会自动更新子组件。这种方法适用于中等规模的应用,可以在组件树中共享状态,但不适用于大型应用或高度复杂的状态管理。
  3. Provider:Provider是Flutter社区中广泛使用的状态管理库,它构建在InheritedWidget之上,提供了一种简化状态共享的方式。它使用了依赖注入的概念,可以在组件树的任何位置共享状态,并自动通知相关的子组件进行更新。Provider支持多种类型的状态管理,包括基于ChangeNotifierStreamValueNotifier等。它适用于中等到大型规模的应用,具有良好的灵活性和性能。
  4. Redux:Redux是一个基于Flux架构的状态管理库,它通过单一的全局状态存储(Store)和纯函数(Reducers)来管理状态。Redux使用了不可变数据和单向数据流的概念,并通过派发操作(Actions)来触发状态的变化。Redux适用于大型应用或需要严格的状态管理和可预测性的场景。
  5. Bloc:Bloc是一种基于Rx(响应式编程)和单向数据流的状态管理库。它使用Stream和Sink来处理输入和输出,并使用事件(Events)和状态(States)的流来管理应用的状态。Bloc适用于复杂的业务逻辑和交互,可以将应用程序的状态和事件分离,并提供了强大的工具和模式来处理异步操作和状态变化。

以上只是一些常见的状态管理方法,还有其他的库和模式可供选择,如GetX、MobX、Riverpod等。选择合适的状态管理方法取决于应用的规模、复杂度和团队的偏好。重要的是根据具体需求选择适合的状态管理方案,以提高应用的可维护性和开发效率。

26 Bloc 和 Cubit 之间有什么区别?

https://pub-web.flutter-io.cn/packages/bloc

Bloc(Business Logic Component)和Cubit(Combination of Bloc and Unit)是Flutter中常用的状态管理库,它们有一些区别,主要体现在以下几个方面:

  1. 复杂性和灵活性: Bloc相对于Cubit来说更加复杂和灵活。Bloc是基于Rx(响应式编程)和单向数据流的状态管理库,它提供了强大的工具和模式来处理复杂的业务逻辑和交互,例如异步操作、副作用管理、事件和状态的转换等。Bloc提供了更大的灵活性,适用于大型应用或需要处理复杂逻辑的场景。而Cubit则是Bloc的一个简化版本,它去除了异步操作和副作用管理的部分,更加轻量级和简单,适用于中小型应用或简化的状态管理需求。
  2. 代码量和学习曲线: 由于Bloc提供了更多的功能和灵活性,它的代码量和学习曲线相对较高。使用Bloc需要熟悉Rx编程概念和一些特定的Bloc模式和约定。相比之下,Cubit的代码量更少,学习曲线更平缓,更容易上手和理解。
  3. 状态的管理方式: 在Bloc中,状态通常由一个或多个Stream来管理,通过派发事件(Events)和监听状态(States)的流来实现状态的变化和更新。而Cubit使用一个单一的State对象来管理状态,并通过emit方法来触发状态的变化。Cubit更加简洁和直接,适用于简单的状态管理。Bloc则提供了更多的灵活性,可以处理更复杂的状态变化和异步操作。
  4. 推荐的使用场景: 由于Cubit相对于Bloc来说更加简单和轻量级,因此它更适合于中小型应用或简化的状态管理需求。如果应用的状态管理相对复杂,需要处理异步操作、副作用等复杂逻辑,那么Bloc是更好的选择。Bloc在大型应用、复杂业务逻辑和团队合作开发中的优势更加明显。

需要注意的是,Bloc和Cubit都是基于相同的概念和模式构建的,它们遵循了单向数据流、不可变状态等原则,并提供了类似的工作方式和API。选择使用哪个取决于应用的需求和开发团队的偏好,以及对复杂性和灵活性的权衡。

有兴趣看猫哥的事情呀,只能说帮助你理解。

27 Flutter GetX 中 obx 和 getBuild 有什么区别

https://pub-web.flutter-io.cn/packages/get

在Flutter GetX库中,obxGetBuilder是用于在UI层观察和更新状态的两个常用组件。它们之间的主要区别如下:

  1. 语法和用法obx(即Observer)是一个Widget,可以将其包裹在需要观察状态变化的部分,通常是一个小部件或一个小部分的UI。它使用了GetBuilder的方式,但提供了更简洁的语法。例如,使用obx可以直接在UI中访问控制器的属性或方法,而无需显式地使用Get.find获取控制器实例。
    ``GetBuilder是另一个Widget,它需要指定一个控制器实例,并通过builder回调函数来构建UI。在GetBuilder中,需要手动访问控制器的属性和方法,并在回调函数中根据需要更新UI。相比之下,obx`提供了一种更简洁和便捷的方式来观察和更新状态。
  2. 重绘粒度obx的重绘粒度更细,它会自动追踪使用的控制器的特定属性,并在这些属性发生变化时进行重绘。这意味着只有与变化的属性相关联的部分会被重新构建,而其他不相关的部分将保持不变,提高了性能。
    ``GetBuilder`的重绘粒度相对较粗,它会在控制器的任何属性发生变化时重新构建整个绑定的UI部分。这可能会导致不必要的重绘,特别是在控制器具有多个属性且只有部分属性发生变化时。

综上所述,obx提供了更简洁、便捷和性能优化的方式来观察和更新状态,特别适用于小规模的状态管理和局部UI更新。而GetBuilder则更适合于需要手动控制重绘粒度或更复杂的状态管理场景。

obx 例子

import 'package:flutter/material.dart';
import 'package:get/get.dart';

// 创建一个控制器类
class MyController extends GetxController {
  var count = 0.obs; // 使用.obs将count变为可观察的(Observable)
  
  void increment() {
    count.value++; // 修改可观察变量的值
  }
}

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final MyController controller = Get.put(MyController()); // 注入控制器

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('GetX obx Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              // 使用obx观察和更新状态
              Obx(() => Text(
                'Count: ${controller.count.value}', // 获取可观察变量的值
                style: TextStyle(fontSize: 24),
              )),
              SizedBox(height: 16),
              ElevatedButton(
                onPressed: () {
                  controller.increment(); // 调用控制器的方法来更新状态
                },
                child: Text('Increment'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

getBuilder 例子

import 'package:flutter/material.dart';
import 'package:get/get.dart';

// 创建一个控制器类
class MyController extends GetxController {
  var count = 0;

  void increment() {
    count++;
    update(); // 手动触发UI更新
  }
}

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final MyController controller = Get.put(MyController()); // 注入控制器

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('GetX GetBuilder Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              // 使用GetBuilder观察和更新状态
              GetBuilder<MyController>(
                builder: (controller) => Text(
                  'Count: ${controller.count}', // 获取控制器的属性值
                  style: TextStyle(fontSize: 24),
                ),
              ),
              SizedBox(height: 16),
              ElevatedButton(
                onPressed: () {
                  controller.increment(); // 调用控制器的方法来更新状态
                },
                child: Text('Increment'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

如果想系统学习 getx

28 Flutter 中什么事依赖注入

在Flutter中,依赖注入(Dependency Injection)是一种设计模式和技术,用于管理和提供应用程序中的依赖关系。依赖注入的目的是解耦组件之间的依赖关系,提高代码的可测试性、可维护性和可扩展性。

在Flutter中,依赖注入可以用于以下几个方面:

  1. 控制器(Controller)和服务(Service)的注入:依赖注入可以帮助我们在需要的地方注入控制器和服务的实例,而不需要手动实例化它们。这样可以降低代码的耦合度,并且方便进行单元测试和模块替换。Flutter GetX库是一个支持依赖注入的流行选择,它提供了一个全局的依赖注入容器,可以通过Get.put()方法将控制器或服务注册为单例,并在需要时使用Get.find()获取它们的实例。
  2. 路由(Route)的注入:依赖注入还可以用于在应用程序中注入路由,以便在不同的页面或组件之间进行导航。例如,可以使用依赖注入容器来注册路由配置,并在需要导航到特定页面时,通过注入路由管理器的方式进行导航。这样可以提高代码的可读性和可维护性,并且可以轻松地更改路由配置而不影响其他部分的代码。
  3. 配置和环境变量的注入:依赖注入可以用于注入应用程序的配置和环境变量,例如API密钥、服务器地址等。通过将这些配置和环境变量注册为依赖项,我们可以在应用程序的不同部分中轻松地访问它们,并且可以根据需要更改它们,而不需要修改大量的代码。
  4. 其他依赖关系的注入:除了上述示例之外,依赖注入还可以用于注入其他类型的依赖关系,如数据库连接、存储库、第三方库等。通过将这些依赖项注册为单例或根据需要创建新的实例,我们可以更好地管理应用程序中的依赖关系,并提供可扩展和可测试的代码结构。

依赖注入在Flutter中用于管理和提供应用程序中的各种依赖关系,包括控制器、服务、路由、配置和其他依赖项。它提供了一种解耦组件之间依赖关系的方式,并提高了代码的可测试性、可维护性和可扩展性。

29 Flutter 组件 Get_it 组件是什么

https://pub-web.flutter-io.cn/packages/get_it

get_it 是一个在 Flutter 中用于依赖注入的第三方库。它提供了一个简单而强大的依赖注入容器,使得在应用程序中管理和访问依赖项变得更加容易。

下面是一些 get_it 库的特点和用法:

  1. 轻量且易于使用get_it 是一个轻量级的库,没有复杂的配置和依赖关系图。它提供了简单的 API,使得注册和获取依赖项变得非常容易。
  2. 支持单例和懒加载get_it 支持将依赖项注册为单例,这意味着同一个依赖项只会被实例化一次,并且在应用程序的不同部分共享使用。另外,get_it 也支持懒加载,即只有在第一次访问依赖项时才会进行实例化。
  3. 支持异步和同步依赖项get_it 提供了对异步和同步依赖项的支持。你可以注册异步工厂函数来创建异步依赖项,也可以注册同步工厂函数来创建同步依赖项。
  4. 支持依赖项解析get_it 允许你在注册依赖项时指定其解析方式。你可以选择自动解析依赖项(默认情况下),也可以手动解析依赖项。手动解析依赖项可以为你提供更多的灵活性和控制权。
  5. 支持注册别名get_it 允许你为依赖项注册别名,以便于识别和访问。这对于管理大量依赖项时非常有用。

使用 get_it 库的基本流程如下:

  1. 在应用程序的启动时,注册需要的依赖项。你可以使用 get_itregisterSingletonregisterFactoryregisterLazySingleton 方法进行注册。
  2. 在需要访问依赖项的地方,使用 get_itget 方法获取依赖项的实例。
  3. 可选地,你可以使用 get_itisRegistered 方法检查依赖项是否已经注册。

get_it 提供了一种简单而强大的方式来进行依赖注入,使得管理和访问依赖项变得更加方便和灵活。它是一个流行的依赖注入库,被广泛用于 Flutter 应用程序的开发中。

猫哥文章视频:

30 Flutter Sliver 是什么解决了什么问题

在Flutter中,Sliver是用于构建灵活和高性能滚动效果的组件。

Sliver解决了以下问题:

  1. 可变大小的滚动元素:传统的滚动组件(如ListView)中的每个子项都具有固定的高度,这限制了滚动元素的灵活性。而Sliver允许每个子项具有不同的高度,从而实现了可变大小的滚动元素。这对于需要动态调整高度的元素(如可伸缩的标题、动态列表项等)非常有用。
  2. 交互式滚动效果:Sliver允许在滚动过程中实现交互式效果和动画。你可以根据滚动位置或其他条件来控制Sliver中的内容,例如淡入淡出效果、透明度变化、放大缩小效果等。这为创建吸顶效果、悬浮按钮、展开折叠效果等提供了便利。
  3. 高性能滚动:使用Sliver构建的滚动效果可以提供更好的性能。Sliver通过延迟构建和回收不可见的元素,以及只构建可见元素来减少内存占用和渲染开销。这在处理大数据集或具有复杂布局的滚动视图时特别有用。
  4. 灵活的自定义:Sliver提供了强大的自定义能力,允许你根据需要定制滚动效果。你可以根据自己的需求创建自定义的Sliver组件,实现各种复杂的滚动效果和交互。

代码示例

CustomScrollView(
  slivers: <Widget>[
    SliverAppBar(
      // 在滚动时隐藏/显示标题栏
      title: Text('Sliver Demo'),
      floating: true,
      // 其他SliverAppBar属性...
    ),
    SliverList(
      delegate: SliverChildBuilderDelegate(
        (BuildContext context, int index) {
          // 构建列表项
          return ListTile(
            title: Text('Item $index'),
          );
        },
        childCount: 20,
      ),
    ),
  ],
)

sliver 复杂使用请看猫哥文章视频:

小结

flutter 面试题到本章已有 30 道题,这都只能算热身, 后面我会整理些实际工作场景中的问答。

感谢阅读本文

如果有什么建议,请在评论中让我知道。我很乐意改进。


flutter 学习路径


© 猫哥 ducafecat.com

end