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

效果如下:

相关推荐
好奇龙猫14 小时前
【大学院-筆記試験練習:数据库(データベース問題訓練) と 软件工程(ソフトウェア)(8)】
学习
石像鬼₧魂石14 小时前
ettercap 命令执行输出的详细解读
linux·学习
Century_Dragon14 小时前
以赛促教,赋能课堂:智能网联汽车仿真教学解决方案
学习
曾浩轩14 小时前
跟着江协科技学STM32之4-2OLED显示屏
c语言·stm32·单片机·嵌入式硬件·学习
kirk_wang14 小时前
Flutter `audio_service` 在鸿蒙端的后台音频服务适配实践
flutter·移动开发·跨平台·arkts·鸿蒙
航Hang*14 小时前
第八章:综合布线技术 —— 进线间和建筑群子系统设计
网络·笔记·学习·设计·期末·光纤
知识分享小能手14 小时前
Ubuntu入门学习教程,从入门到精通, Ubuntu 22.04中的C/C++编程(18)
c语言·学习·ubuntu
Android-Flutter14 小时前
android compose Drawer抽屉 使用
android·kotlin
张风捷特烈14 小时前
如何用 Dart 写个自己的MCP服务
flutter·dart·mcp