Flutter最全面试题大全

在理解这些问题之前,建议看一下Flutter架构原理,如下链接:

https://blog.csdn.net/wang_yong_hui_1234/article/details/130427887?spm=1001.2014.3001.5501

目录

    • 一. 有个Text节点,由于文字内容过多,发生了溢出错误,该如何解决?
    • 二.Widget、Element、RenderObject三者之间的关系?
    • 三.什么是有状态(Stateful)和无状态(Stateless)Widget?它们之间有什么区别?
    • 四.Flutter中的路由是什么?如何导航到新的页面?
    • 五.什么是Flutter的状态管理?有哪些状态管理库可用?
    • 六.请解释Flutter的Widget生命周期
    • 七.什么是Flutter的Key?它们的作用是什么?
    • 八.Dart是单线程模型,如何运行的?
    • 九.var、final、const、late介绍
    • 十.Future,Steam,async,await 区别和原理?
    • 十一.空检查问题
    • 十二.InheritedWidget如何实现共享的
    • 十三.Flutter如何支持原生View的

      一. 有个Text节点,由于文字内容过多,发生了溢出错误,该如何解决?

      1.使用overflow属性

      Text(
        '这是一个很长的文本内容,可能会导致溢出错误。',
        overflow: TextOverflow.ellipsis, // 或者 TextOverflow.fade
      )
      

      2.使用maxLines属性

      Text(
        '这是一个很长的文本内容,可能会导致溢出错误。',
        maxLines: 2,
      )
      

      3.使用Expanded或Flexible

      Row(
        children: [
          Expanded(
            child: Text(
              '这是一个很长的文本内容,可能会导致溢出错误。',
              overflow: TextOverflow.ellipsis,
            ),
          ),
        ],
      )
      

      4.使用ListView或SingleChildScrollView

      SingleChildScrollView(
        child: Text(
          '这是一个很长的文本内容,可能会导致溢出错误。',
        ),
      )
      

      二.Widget、Element、RenderObject三者之间的关系?

      • Widget是UI的声明式描述,它们通常是层次结构的顶部。
      • Element是Widget的实例,它们构成了渲染树,并管理了Widget的生命周期和状态。
      • RenderObject是渲染树的实际工作单位,负责执行实际的绘制和布局。

        三.什么是有状态(Stateful)和无状态(Stateless)Widget?它们之间有什么区别?

        1. 有状态(Stateful)Widget:

          • 有状态Widget是一种可以包含可变状态的Widget类型。
          • 当其内部状态(state)发生变化时,可以通知Flutter框架进行重新构建,并且可以在多次构建之间保留状态。
          • 通常在需要响应用户交互或数据更新时使用,比如表单、按钮、动画等。
          • 有状态Widget通常包括两个类:一个是继承自StatefulWidget的Widget类,另一个是继承自State的状态类,状态类包含了Widget的可变状态。

            示例:

            class MyStatefulWidget extends StatefulWidget { @override
              _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
            }
            class _MyStatefulWidgetState extends State { int _counter = 0;
              void _incrementCounter() { setState(() { _counter++;
                });
              }
              @override
              Widget build(BuildContext context) { return Column(
                  children: [
                    Text('Counter: $_counter'),
                    ElevatedButton(
                      onPressed: _incrementCounter,
                      child: Text('Increment'),
                    ),
                  ],
                );
              }
            }
            
          • 无状态(Stateless)Widget:

            • 无状态Widget是一种不包含可变状态的Widget类型。
            • 它们通常用于展示静态内容或不需要重新构建的部分,因为它们在构建后不会发生变化。
            • 无状态Widget是不可变的,一旦构建就不能再修改内部状态。
            • 通常用于构建UI的静态部分,以提高性能。

              示例:

              class MyStatelessWidget extends StatelessWidget { @override
                Widget build(BuildContext context) { return Text('Hello, World!');
                }
              }
              

        总的来说,有状态Widget适用于需要管理可变状态的场景,而无状态Widget适用于静态内容的展示。使用它们的组合可以有效地构建复杂的用户界面,同时保持性能和可维护性。

        四.Flutter中的路由是什么?如何导航到新的页面?

        路由是用于导航和管理不同页面(或称为屏幕)之间切换的机制。Flutter的路由系统允许您在应用程序中创建多个页面,并实现页面之间的导航。

        Flutter中有两种常见的路由:命名路由和普通(非命名)路由

        MaterialApp(
          routes: { '/': (context) => HomeScreen(),
            '/second': (context) => SecondScreen(),
          },
          // ...
        )
        // 导航到命名路由
        Navigator.pushNamed(context, '/second');
        
        // 导航到新页面
        Navigator.push(
          context,
          MaterialPageRoute(builder: (context) => SecondScreen()),
        );
        

        第三方路由框架:

        go_router:https://pub.dev/packages?q=go_router

        auto_route:https://pub.dev/packages/auto_route

        五.什么是Flutter的状态管理?有哪些状态管理库可用?

        Flutter应用程序通常会包含许多不同的小部件,这些小部件可能需要访问和共享数据,而状态管理的目标是使这个过程更加有组织和高效。

        状态管理:

        • InheritedWidget:InheritedWidget是Flutter框架提供的一种状态共享机制。它允许您在小部件树中共享数据,以便子小部件可以轻松访问共享状态。这在跨多个小部件传递数据时非常有用。
        • Provider:Provider是一个开源的Flutter状态管理库,它建立在InheritedWidget之上,并提供了更简化的数据共享和更新方式。它通常与Consumer小部件一起使用,以便小部件只在相关数据发生变化时重新构建。
        • flutter_bloc :它提供了用于实现BLoC设计模式的工具和类,提供了一种清晰、可测试和可维护的方式来管理Flutter应用程序的状态和业务逻辑。它将状态与UI分离,使得应用程序更易于扩展和修改

          六.请解释Flutter的Widget生命周期

          initState

          当插入渲染树的时候调用,这个函数在生命周期中只调用一次。这里可以做一些初始化工作,比如初始化State的变量。

          didChangeDependencies

          在Widget构建后,如果依赖的InheritedWidget发生变化,则会调用此方法。通常用于处理数据依赖关系的变化

          didUpdateWidget

          当组件的状态改变的时候就会调用didUpdateWidget,比如调用了setState。

          deactivate

          这通常用于在Widget不再可见或处于非活动状态时执行一些清理工作。

          dispose

          在Widget从Widget树中移除后,会调用dispose方法,用于释放资源和取消订阅。

          七.什么是Flutter的Key?它们的作用是什么?

          Key是一个重要的概念,用于标识Widget并确保它们在Widget树中的唯一性。用于标识和查找Widget。

          常用的key:

          • ValueKey: 通过一个特定的值作为标识。
          • ObjectKey: 通过一个对象作为标识。
          • GlobalKey: 全局标识,通常用于跨Widget树中的状态共享。

            ValueKey使用:

            Widget build(BuildContext context) { return ListView(
                children: [
                  ListTile(
                    key: ValueKey('item_1'),
                    title: Text('Item 1'),
                  ),
                  ListTile(
                    key: ValueKey('item_2'),
                    title: Text('Item 2'),
                  ),
                  // ...
                ],
              );
            }
            

            在这个示例中,我们为每个ListTile指定了一个不同的ValueKey,以确保它们在列表中的位置可以被正确标识。

            ObjectKey使用

            final myObject = MyCustomObject(); // 创建一个自定义对象
            Widget build(BuildContext context) { return ListView(
                children: [
                  ListTile(
                    key: ObjectKey(myObject), // 使用ObjectKey关联自定义对象
                    title: Text('Item 1'),
                  ),
                  ListTile(
                    key: ObjectKey('some_string'), // 使用ObjectKey关联字符串
                    title: Text('Item 2'),
                  ),
                  // ...
                ],
              );
            }
            

            在这个示例中,我们创建了一个自定义对象myObject,并使用ObjectKey将其关联到ListTile上。这意味着当myObject发生变化时,与其关联的ListTile将被认为需要更新。

            八.Dart是单线程模型,如何运行的?

            Dart 在单线程中是以消息循环机制来运行的,其中包含两个任务队列,一个是“微任务队列” microtask queue,另一个叫做“事件队列” event queue。

            九.var、final、const、late介绍

            • var: 定义的变量会自动推断类型,如果没有初始化,那么它的值是nil。在声明时赋值,相当于确定了类型。
            • final: 可以先声明再次赋值,但是只能赋值一次
            • const: 修饰常量,声明的时候就得赋值
            • late: 延迟初始化变量,如果这个变量没有被使用的话就不会被初始化。

              十.Future,Steam,async,await 区别和原理?

              Future

              Future 表示一个可能会在未来完成的操作,通常用于执行一些需要时间的任务,如网络请求、文件读写、计算等。

              Future 具有以下几种状态:

              • 未完成:Future 正在执行或等待执行;
              • 完成:Future 成功完成并返回一个值;
              • 失败:Future 执行时发生了错误;

                try/catch未能够捕获future中的异常,因此future中的异常只能通过catchError()或在then()方法中传入可选参数onError来进行捕获和处理。

                相关方法

                • Future.wait():等待多个Future全部完成后才返回结果

                  async/await

                  如果说Future是一个盒子,当你使用Future直接返回给你个盒子,并且不会阻塞。而async/await就是为了打开这个盒子,拿到执行的结果。

                  async 和 await 的核心原理是,它们通过暂停和恢复异步函数的执行,使得程序能够继续处理其他任务,而不会被异步操作所阻塞。

                  十一.空检查问题

                  1.name??‘默认’

                  这段代码的含义:当name为空时给个默认值。

                  2. name!

                  如果name为空,这段代码会出现什么情况:在debug模式下会显示红色,在release模式下显示一块灰色

                  3.name?.length

                  如果 name 为 null,则 length 为 null;否则为 name.length 的值

                  十二.InheritedWidget如何实现共享的

                  其实在 Element 的内部有一个 Map _inheritedWidgets; 参数,

                  _inheritedWidgets 一般情况下是空的,只有当父控件是 InheritedWidget 或者本身是 InheritedWidget 时,它才会被初始化,而当父控件是 InheritedWidget 时,这个 Map 会被一级一级往下传递与合并。

                  十三.Flutter如何支持原生View的

                  Flutter 支持两种集成模式:虚拟显示模式 (Virtual displays) 和混合集成模式 (Hybrid composition)

                  虚拟显示模式 (Virtual displays)

                  这种方式使用比较简单:

                  AndroidView(
                      viewType: viewType,
                      layoutDirection: TextDirection.ltr,
                      creationParams: creationParams,
                      creationParamsCodec: const StandardMessageCodec(),
                    )
                  

                  以 Android 为例,Android 上是利用了副屏显示的底层逻辑,使用 VirtualDisplay 类,创建一个虚拟显示器,需要调用 DisplayManager 的 createVirtualDisplay() 方法,将虚拟显示器的内容渲染在一个内存的 Surface 上 ,生成一个唯一的 textureId 。

                  如下图,之后渲染时将 textureId 传递给 Dart 层,渲染引擎会根据 textureId , 获取到内存里已渲染数据,绘制到 AndroidView 上进行显示。

                  混合集成模式 (Hybrid composition)

                  会将原生的 android.view.View 附加到视图层次结构中。因此,键盘处理和无障碍功能是开箱即用的。在 Android 10 之前,此模式可能会大大降低 Flutter UI 的帧吞吐量 (FPS)

                  相对会比直接使用 AndroidView 在代码上更复杂一点, 需要使用到 PlatformViewLink、 AndroidViewSurface 和 PlatformViewsService 这三个对象

                  Widget build(BuildContext context) { // This is used in the platform side to register the view.
                    const String viewType = '';
                    // Pass parameters to the platform side.
                    const Map creationParams = {};
                    return PlatformViewLink(
                      viewType: viewType,
                      surfaceFactory: (context, controller) { return AndroidViewSurface(
                          controller: controller as AndroidViewController,
                          gestureRecognizers: const >{},
                          hitTestBehavior: PlatformViewHitTestBehavior.opaque,
                        );
                      },
                      onCreatePlatformView: (params) { return PlatformViewsService.initSurfaceAndroidView(
                          id: params.id,
                          viewType: viewType,
                          layoutDirection: TextDirection.ltr,
                          creationParams: creationParams,
                          creationParamsCodec: const StandardMessageCodec(),
                          onFocus: () { params.onFocusChanged(true);
                          },
                        )
                          ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
                          ..create();
                      },
                    );
                  }