Android学Flutter学习笔记 第三节 Android视角认知Flutter(触摸事件,List,Text,Input)

序言

认识flutter已经到了第三节。我们了解到了flutter的启动入口,weight类别和常见的类型,页面之间的跳转,资源文件的配置,如何实现weight的排列、以及如何和Android互动,下面我们继续已Android视角认识flutter中的细节。

手势检测和触摸事件处理

如何在 Flutter 中给一个组件添加 onClick 监听器?

在安卓系统中,你可以通过调用 "setOnClickListener" 方法,将 onClick(点击事件)附加到按钮等视图上。

在 Flutter 中,有两种添加触摸监听器的方法:

1、如果微件(Widget)支持事件检测,可以向其传递一个函数,并在该函数中进行处理。例如,ElevatedButton 具有 onPressed 参数:

kotlin 复制代码
@override
Widget build(BuildContext context) {
  return ElevatedButton(
    onPressed: () {
      developer.log('click');
    },
    child: const Text('Button'),
  );
}

2、如果 Widget 不支持事件检测,请将该 widget 包裹在 GestureDetector 中,并向 onTap 参数传递一个函数。

kotlin 复制代码
class SampleTapApp extends StatelessWidget {
  const SampleTapApp({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: GestureDetector(
          onTap: () {
            developer.log('tap');
          },
          child: const FlutterLogo(size: 200),
        ),
      ),
    );
  }
}

如何处理小部件上的其他手势?

使用 GestureDetector,你可以监听各种各样的手势,例如:

Tap:

onTapDown - 一个可能触发点击操作的指针已在屏幕特定位置接触屏幕。

onTapUp - 一个触发点击操作的指针已在屏幕特定位置停止接触屏幕。

onTap - 点击操作已发生。

onTapCancel - 先前触发onTapDown的指针将不会导致点击操作。


Double tap

onDoubleTap 用户快速连续两次点击了屏幕上的同一位置

Long press

onLongPress 指针长时间与屏幕上的同一位置保持接触。

Vertical drag

onVerticalDragStart - 指针已接触屏幕并可能开始垂直移动。

onVerticalDragUpdate - 与屏幕接触的指针在垂直方向上进一步移动。

onVerticalDragEnd - 先前与屏幕接触并垂直移动的指针已不再接触屏幕,且在停止接触时具有特定速度。

Horizontal drag

onHorizontalDragStart - 指针已接触屏幕,并可能开始水平移动。

onHorizontalDragUpdate - 与屏幕接触的指针在水平方向上已进一步移动。

onHorizontalDragEnd - 先前与屏幕接触并水平移动的指针已不再接触屏幕,且在停止接触时具有特定速度。

下面是一个双击和旋转动画结合的例子

kotlin 复制代码
class SampleApp extends StatefulWidget {
  const SampleApp({super.key});

  @override
  State<SampleApp> createState() => _SampleAppState();
}

class _SampleAppState extends State<SampleApp>
    with SingleTickerProviderStateMixin {
  late AnimationController controller;
  late CurvedAnimation curve;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 2000),
    );
    curve = CurvedAnimation(parent: controller, curve: Curves.easeIn);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: GestureDetector(
          onDoubleTap: () {
            if (controller.isCompleted) {
              controller.reverse();
            } else {
              controller.forward();
            }
          },
          child: RotationTransition(
            turns: curve,
            child: const FlutterLogo(size: 200),
          ),
        ),
      ),
    );
  }
}

ListView

Flutter 中 ListView 的替代方案是什么?

Flutter 中与 ListView 等效的组件依然是ListView!

在 Android 的 ListView 中,你需要创建一个适配器并将其传入 ListView,ListView 会根据适配器返回的内容来渲染每一行。不过,你必须确保对行进行回收,否则会出现各种奇怪的视觉故障和内存问题。

由于 Flutter 的不可变组件模式,你可以将一个组件列表传递给 ListView,而 Flutter 会负责确保滚动既快速又流畅。

kotlin 复制代码
import 'package:flutter/material.dart';

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample App',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: const SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Sample App')),
      body: ListView(children: _getListData()),
    );
  }

  List<Widget> _getListData() {
    List<Widget> widgets = [];
    for (int i = 0; i < 100; i++) {
      widgets.add(
        Padding(padding: const EdgeInsets.all(10), child: Text('Row $i')),
      );
    }
    return widgets;
  }
}

如何给item设置点击

在 Android 中,ListView 有一个用于查明哪个条目被点击的方法,即 "onItemClickListener"。在 Flutter 中,使用传入的小部件所提供的触摸处理。

kotlin 复制代码
import 'dart:developer' as developer;

import 'package:flutter/material.dart';

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample App',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: const SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Sample App')),
      body: ListView(children: _getListData()),
    );
  }

  List<Widget> _getListData() {
    List<Widget> widgets = [];
    for (int i = 0; i < 100; i++) {
      widgets.add(
        GestureDetector(
          onTap: () {
            developer.log('row tapped');
          },
          child: Padding(
            padding: const EdgeInsets.all(10),
            child: Text('Row $i'),
          ),
        ),
      );
    }
    return widgets;
  }
}

如何动态更新 ListView?

在安卓系统上,你需要更新适配器并调用 notifyDataSetChanged 方法。

在 Flutter 中,如果你要在 setState () 里更新 widget 列表,你会很快发现数据在视觉上并没有发生变化。这是因为当调用 setState () 时,Flutter 渲染引擎会查看 widget 树,判断是否有内容发生了改变。当它检查到你的 ListView 时,会执行一个 == 检查,然后判定这两个 ListView 是相同的。既然没有变化,也就不需要进行更新了。

要以简单的方式更新你的 ListView,请在 setState () 中创建一个新的 List,并将数据从旧列表复制到新列表。虽然这种方法很简单,但不建议用于大型数据集,如下一个示例所示。

kotlin 复制代码
import 'dart:developer' as developer;

import 'package:flutter/material.dart';

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample App',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: const SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List<Widget> widgets = [];

  @override
  void initState() {
    super.initState();
    for (int i = 0; i < 100; i++) {
      widgets.add(getRow(i));
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Sample App')),
      body: ListView(children: widgets),
    );
  }

  Widget getRow(int i) {
    return GestureDetector(
      onTap: () {
        setState(() {
          widgets = List.from(widgets);
          widgets.add(getRow(widgets.length));
          developer.log('row $i');
        });
      },
      child: Padding(padding: const EdgeInsets.all(10), child: Text('Row $i')),
    );
  }
}

推荐的、高效且有效的构建列表的方法是使用 ListView.Builder。当你有一个动态列表或包含大量数据的列表时,这种方法非常好用。它本质上相当于 Android 上的 RecyclerView,会自动为你回收列表元素:

kotlin 复制代码
import 'dart:developer' as developer;

import 'package:flutter/material.dart';

void main() {
  runApp(const SampleApp());
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample App',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: const SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  const SampleAppPage({super.key});

  @override
  State<SampleAppPage> createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List<Widget> widgets = [];

  @override
  void initState() {
    super.initState();
    for (int i = 0; i < 100; i++) {
      widgets.add(getRow(i));
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Sample App')),
      body: ListView.builder(
        itemCount: widgets.length,
        itemBuilder: (context, position) {
          return getRow(position);
        },
      ),
    );
  }

  Widget getRow(int i) {
    return GestureDetector(
      onTap: () {
        setState(() {
          widgets.add(getRow(widgets.length));
          developer.log('row $i');
        });
      },
      child: Padding(padding: const EdgeInsets.all(10), child: Text('Row $i')),
    );
  }
}

不要创建 "ListView",而是创建一个 ListView.builder,它接受两个关键参数:列表的初始长度和一个 ItemBuilder 函数。

ItemBuilder 函数类似于 Android 适配器中的 getView 函数,它接收一个位置参数,并返回你希望在该位置渲染的行。

最后,但最重要的是,注意 onTap () 函数不再重新创建列表,而是向列表中添加元素

Text

我如何在我的文本部件上设置自定义字体?

在 Android SDK(截至 Android O 版本)中,你需要创建一个字体资源文件,并将其传入 TextView 的 FontFamily 参数中。

在 Flutter 中,将字体文件放在一个文件夹中,并在 pubspec.yaml 文件中引用它,类似于导入图片的方式。

kotlin 复制代码
fonts:
   - family: MyCustomFont
     fonts:
       - asset: fonts/MyCustomFont.ttf
       - style: italic
kotlin 复制代码
@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: const Text('Sample App')),
    body: const Center(
      child: Text(
        'This is a custom font text',
        style: TextStyle(fontFamily: 'MyCustomFont'),
      ),
    ),
  );
}

我该如何设置我的文本小部件的样式?

除了字体,你还可以自定义文本组件上的其他样式元素。文本组件的 style 参数接受一个 TextStyle 对象,在该对象中,你可以自定义许多参数,例如:

color

decoration

decorationColor

decorationStyle

fontFamily

fontSize

fontStyle

fontWeight

hashCode

height

inherit

letterSpacing

textBaseline

wordSpacing

效果如下:

相关推荐
火柴就是我7 小时前
让我们实现一个更好看的内部阴影按钮
android·flutter
王晓枫7 小时前
flutter接入三方库运行报错:Error running pod install
前端·flutter
砖厂小工14 小时前
用 GLM + OpenClaw 打造你的 AI PR Review Agent — 让龙虾帮你审代码
android·github
张拭心14 小时前
春节后,有些公司明确要求 AI 经验了
android·前端·人工智能
张拭心14 小时前
Android 17 来了!新特性介绍与适配建议
android·前端
shankss15 小时前
Flutter 下拉刷新库 pull_to_refresh_plus 设计与实现分析
flutter
Kapaseker17 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴17 小时前
Android17 为什么重写 MessageQueue
android
忆江南1 天前
iOS 深度解析
flutter·ios
明君879971 天前
Flutter 实现 AI 聊天页面 —— 记一次 Markdown 数学公式显示的踩坑之旅
前端·flutter