Flutter TV Android端开发技巧详细教程

目录
  • 前言
  • 开发思路
  • 先上效果
  • 开发细节
    • 使用RawKeyboardListener
    • Provider层对事件进行处理
    • 注意
  • 总结
  • 文件参考
    • TV keyCode详解

前言

最近公司有了新的业务,把现有Flutter Android项目应用到TV上去,这不,Asscre的活就来了。

本文详细说明Flutter for TV的两种实现方式,能力有限,不足之处欢迎指点,哈哈哈

开发思路

在开发之前,我们先设定一下我们的思路。

即,如何对原有程序代码侵入式最小、性能最佳、可玩性更高做出设定。

那么,通过上面的设定,我们在Flutter Widget中就发现了两个东西:

  • RawKeyboardListener
  • InkWell和其他Android TV配置

先上效果

可玩性、可塑性更高的RawKeyboardListener解决方案效果

对原有程序修改最小的InkWell和其他Android TV配置解决方案效果

开发细节

可玩性、可塑性更高的RawKeyboardListener解决方案

使用RawKeyboardListener

RawKeyboardListener(
  focusNode: d.focusNode, // 配置focusNode
  onKey: (RawKeyEvent event) =>
      context.read<HomePageContentWidgetProvider>().focusEventHandler(event, context, d), // 对特殊事件进行监听和处理
  child: Container(
    height: 190,
    width: 190,
    decoration: BoxDecoration(
      border: Border.all(
          width: 2,
          color: d.focusNode.hasFocus ? Colors.blue : Colors.transparent),
      borderRadius: BorderRadius.circular(20),
      color: Colors.white.withAlpha(20),
    ),
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        Image.asset(
          d.img,
          height: 80,
        ),
        SizedBox(height: 20),
        Text(
          d.name,
          style: TextStyle(
            color: Colors.white,
            fontSize: 32,
          ),
        ),
      ],
    ),
  ),
),

Provider层对事件进行处理

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tv_test/pages/memory_page/memory_page.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class HomePageContentWidgetProvider
    with ChangeNotifier {
  bool init = false;
  double maxWScreen = 0; // 按钮距离屏幕右侧最大边界
  double minWScreen = 60.w; // 按钮距离屏幕最左侧距离边界
  final List<HomePageMakeBtn> makeBtnList = [
    HomePageMakeBtn('lib/assets/img/youtube.png', 'You Tube', '', FocusNode()),
    HomePageMakeBtn('lib/assets/img/apple.png', 'Apple', '', FocusNode()),
    HomePageMakeBtn('lib/assets/img/facebook.png', 'Facebook', '', FocusNode()),
    HomePageMakeBtn('lib/assets/img/douyin.png', 'Tik Tok', '', FocusNode()),
    HomePageMakeBtn('lib/assets/img/mi.png', 'MI', '', FocusNode()),
    HomePageMakeBtn('lib/assets/img/huawei.png', 'Hua Wei', '', FocusNode()),
    HomePageMakeBtn('lib/assets/img/youtube.png', 'TTT', '', FocusNode()),
    HomePageMakeBtn('lib/assets/img/apple.png', 'DDDD', '', FocusNode()),
    HomePageMakeBtn('lib/assets/img/facebook.png', 'FFFF', '', FocusNode()),
    HomePageMakeBtn('lib/assets/img/douyin.png', 'AAAA', '', FocusNode()),
    HomePageMakeBtn('lib/assets/img/mi.png', 'QQQQQ', '', FocusNode()),
    HomePageMakeBtn('lib/assets/img/huawei.png', 'WWWW', '', FocusNode()),
    HomePageMakeBtn('lib/assets/img/youtube.png', 'EEEEE', '', FocusNode()),
    HomePageMakeBtn('lib/assets/img/apple.png', 'RRRRR', '', FocusNode()),
    HomePageMakeBtn('lib/assets/img/facebook.png', 'YYYYYY', '', FocusNode()),
    HomePageMakeBtn('lib/assets/img/douyin.png', 'UUUUUU', '', FocusNode()),
    HomePageMakeBtn('lib/assets/img/mi.png', 'SSSSS', '', FocusNode()),
    HomePageMakeBtn('lib/assets/img/huawei.png', 'VVVV', '', FocusNode()),
  ];
  HomePageContentWidgetProvider(BuildContext context) {
    maxWScreen = MediaQuery.of(context).size.width - 246.w;
    // setMakeFocusAddListener();
    if (!init) {
      makeBtnList.first.focusNode.requestFocus();
      init = true;
    }
  }
  setMakeFocusAddListener() {
    for (int i = 0; i < makeBtnList.length; i++) {
      makeBtnList[i].focusNode.addListener(() {
        if (makeBtnList[i].focusNode.hasFocus) {
          // notifyListeners();
          print(
              '====${makeBtnList[i].name} : ${makeBtnList[i].focusNode.hasFocus}');
        }
      });
    }
  }
  setMakeFocusDispose() {
    for (var item in makeBtnList) {
      item.focusNode.removeListener(() {});
      item.focusNode.dispose();
    }
  }
  focusEventHandler(
      RawKeyEvent event, BuildContext context, HomePageMakeBtn param) async {
    /// 只处理按键按下的事件
    if (event.data is RawKeyEventDataAndroid &&
        event.runtimeType.toString() == 'RawKeyDownEvent') {
      CustomRawKeyEventDataAndroid _d =
          CustomRawKeyEventDataAndroid.format(event.data);
      /// 对按下确定键和中心键进行处理
      if (_d.keyCode == 23 || _d.keyCode == 66) {
        Navigator.of(context).push(
            MaterialPageRoute(builder: (_) => MemoryPage(title: param.name)));
      } else {
        // for (var e in makeBtnList) {
        //   print('${e.name} : ${e.focusNode.hasFocus}');
        // }
        /// 对左键进行处理
        if (_d.keyCode == 21) {
          await keyCodeDpadLeft(context, param);
        }
        /// 对右键进行处理
        if (_d.keyCode == 22) {
          await keyCodeDpadRight(context, param);
        }
        notifyListeners();
      }
    }
  }
  /// 对左键进行处理
  keyCodeDpadLeft(BuildContext context, HomePageMakeBtn param) async {
    /// 首位边界处理
    final int _idx = makeBtnList.indexWhere((e) => e == param);
    if (_idx == 0) return;
    final int _nextIndex = _idx + 1;
    if ((_nextIndex % 7) == 1) {
      HomePageMakeBtn _nextNode = makeBtnList[_idx - 1];
      print(_nextNode.name);
      await Future.delayed(const Duration(milliseconds: 20));
      _nextNode.focusNode.requestFocus();
    }
  }
  /// 对右键进行处理
  keyCodeDpadRight(BuildContext context, HomePageMakeBtn param) async {
    final int _idx = makeBtnList.indexWhere((e) => e == param);
    /// 末位边界处理
    if (_idx == (makeBtnList.length - 1)) return;
    final int _nextIndex = _idx + 1;
    if ((_nextIndex % 7) == 0) {
      HomePageMakeBtn _nextNode = makeBtnList[_nextIndex];
      await Future.delayed(const Duration(milliseconds: 20));
      _nextNode.focusNode.requestFocus();
    }
  }
  @override
  void dispose() {
    setMakeFocusDispose();
    super.dispose();
  }
}
class HomePageMakeBtn {
  final String img;
  final String name;
  final String routerName;
  final FocusNode focusNode;
  HomePageMakeBtn(this.img, this.name, this.routerName, this.focusNode);
}
class CustomRawKeyEventDataAndroid {
  final int flags;
  final int codePoint;
  final int plainCodePoint;
  /// case 19: KEY_UP
  /// case 20: KEY_DOWN
  /// case 21: KEY_LEFT
  /// case 22: KEY_RIGHT
  /// case 23: KEY_CENTER
  final int keyCode;
  final int scanCode;
  final int metaState;
  CustomRawKeyEventDataAndroid(this.flags, this.codePoint, this.plainCodePoint,
      this.keyCode, this.scanCode, this.metaState);
  static CustomRawKeyEventDataAndroid format(d) {
    return CustomRawKeyEventDataAndroid(d.flags, d.codePoint, d.plainCodePoint,
        d.keyCode, d.scanCode, d.metaState);
  }
}

注意

我们可以看到在处理左键和右键的时候我们用了

这是为什么呢?

那是因为在实际效果中,我们requestFocus操作的时候,Flutter的机制会首先触发一次requestFocus,然后再触发一次requestFocus,一共两次,这就与我们的预想就有冲突了。

例如:

使用按键末尾向右时,系统触发的focus到UUUUU这个按钮,我们的实际预想的是到YYYYY即可。

使用按键首位向左时,同样会跨两个focus。

目前Asscre并没有找到很好的解决方案,但使用await Future delayed可以舒缓一下这不人性的操作。

对原有程序修改最小的InkWell和其他Android TV配置解决方案

首先,我们需要在AndroidManifest.xml 设置LEANBACK_LAUNCHER告诉平台我们的程序是一个电视应用程序

<intent-filter>
    <action android:name="android.intent.action.MAIN"/>
    <category android:name="android.intent.category.LEANBACK_LAUNCHER"/> // 新增这一句
    <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>

然后,我们在Main入口文件中添加 Shortcuts用于我们的程序响应我们的遥控器指令。

return Shortcuts(
  shortcuts: <LogicalKeySet, Intent>{
    LogicalKeySet(LogicalKeyboardKey.select): ActivateIntent(),
  },
  child: MaterialApp(
  ...
);

最后,使用InkWell来获取焦点设置用户遥控点击的效果,其中focusColor帮助我们提醒用户此时的按钮位置。

    return Material(
      color: Colors.white.withAlpha(20),
      child: InkWell(
        focusColor: Colors.deepOrange.withAlpha(80),
        onTap: () => Navigator.of(context)
            .push(MaterialPageRoute(builder: (_) => MemoryPage(title: d.name))),
        child: SizedBox(
          height: 190,
          width: 190,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Image.asset(
                d.img,
                height: 80,
              ),
              SizedBox(height: 20),
              Text(
                d.name,
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 32,
                ),
              ),
            ],
          ),
        ),
      ),
    );

总结

上述两种解决方案中,大家可以根据自己(boss)的喜好或者业务需求选择一种使用。

在需要复杂的自定义的业务情况下,推荐使用RawKeyboardListener的解决方案,可以做出很多酷炫的效果,譬如按键事件触发时,focus住的widget可以做出放大、渐变等等效果,这有助于提升用户的体验。

但,要是在现有的业务逻辑上,在少量调整后就可使用上述中的InkWell的解决方案。

文件参考

TV keyCode详解

name keycode 说明
KEYCODE_UNKNOWN 0
--------------------------------------- ----- --------------
KEYCODE_SOFT_LEFT 1
KEYCODE_SOFT_RIGHT 2
KEYCODE_HOME 3 HOME键
KEYCODE_BACK 4 返回键
KEYCODE_CALL 5 拨号键
KEYCODE_ENDCALL 6 挂机键
KEYCODE_0 7
KEYCODE_1 8
KEYCODE_2 9
KEYCODE_3 10
KEYCODE_4 11
KEYCODE_5 12
KEYCODE_6 13
KEYCODE_7 14
KEYCODE_8 15
KEYCODE_9 16
KEYCODE_STAR 17 按键 *
KEYCODE_POUND 18 按键 #
KEYCODE_DPAD_UP 19 向上
KEYCODE_DPAD_DOWN 20 向下
KEYCODE_DPAD_LEFT 21 向左
KEYCODE_DPAD_RIGHT 22 向右
KEYCODE_DPAD_CENTER 23 确定键
KEYCODE_VOLUME_UP 24 音量增加键
KEYCODE_VOLUME_DOWN 25 音量减小键
KEYCODE_POWER 26 电源键
KEYCODE_CAMERA 27 拍照键
KEYCODE_CLEAR 28
KEYCODE_A 29
KEYCODE_B 30
KEYCODE_C 31
KEYCODE_D 32
KEYCODE_E 33
KEYCODE_F 34
KEYCODE_G 35
KEYCODE_H 36
KEYCODE_I 37
KEYCODE_J 38
KEYCODE_K 39
KEYCODE_L 40
KEYCODE_M 41
KEYCODE_N 42
KEYCODE_O 43
KEYCODE_P 44
KEYCODE_Q 45
KEYCODE_R 46
KEYCODE_S 47
KEYCODE_T 48
KEYCODE_U 49
KEYCODE_V 50
KEYCODE_W 51
KEYCODE_X 52
KEYCODE_Y 53
KEYCODE_Z 54
KEYCODE_COMMA 55 按键 ,
KEYCODE_PERIOD 56 按键 .
KEYCODE_ALT_LEFT 57
KEYCODE_ALT_RIGHT 58
KEYCODE_SHIFT_LEFT 59
KEYCODE_SHIFT_RIGHT 60
KEYCODE_TAB 61 Tab键
KEYCODE_SPACE 62 空格键
KEYCODE_SYM 63
KEYCODE_EXPLORER 64
KEYCODE_ENVELOPE 65
KEYCODE_ENTER 66 回车键
KEYCODE_DEL 67 退格键
KEYCODE_GRAVE 68 按键 `
KEYCODE_MINUS 69 按键-
KEYCODE_EQUALS 70 按键 =
KEYCODE_LEFT_BRACKET 71 按键 [
KEYCODE_RIGHT_BRACKET 72 按键 ]
KEYCODE_BACKSLASH 73 按键 \
KEYCODE_SEMICOLON 74 按键 ,
KEYCODE_APOSTROPHE 75 按键 ''单引号
KEYCODE_SLASH 76 按键 /
KEYCODE_AT 77 按键 @
KEYCODE_NUM 78
KEYCODE_HEADSETHOOK 79
KEYCODE_FOCUS 80 拍照对焦键
KEYCODE_PLUS 81 按键+
KEYCODE_MENU 82 菜单键
KEYCODE_NOTIFICATION 83 通知键
KEYCODE_SEARCH 84
KEYCODE_MEDIA_PLAY_PAUSE 85 多媒体键 播放/暂停
KEYCODE_MEDIA_STOP 86 多媒体键 暂停
KEYCODE_MEDIA_NEXT 87 多媒体键 下一首
KEYCODE_MEDIA_PREVIOUS 88 多媒体键 上一首
KEYCODE_MEDIA_REWIND 89 多媒体键 快退
KEYCODE_MEDIA_FAST_FORWARD 90 多媒体键 快进
KEYCODE_MUTE 91 话筒静音键
KEYCODE_PAGE_UP 92 向上翻页键
KEYCODE_PAGE_DOWN 93 向下翻页键
KEYCODE_PICTSYMBOLS 94
KEYCODE_SWITCH_CHARSET 95
KEYCODE_BUTTON_A 96
KEYCODE_BUTTON_B 97
KEYCODE_BUTTON_C 98
KEYCODE_BUTTON_X 99
KEYCODE_BUTTON_Y 100
KEYCODE_BUTTON_Z 101
KEYCODE_BUTTON_L1 102
KEYCODE_BUTTON_R1 103
KEYCODE_BUTTON_L2 104
KEYCODE_BUTTON_R2 105
KEYCODE_BUTTON_THUMBL 106
KEYCODE_BUTTON_THUMBR 107
KEYCODE_BUTTON_START 108
KEYCODE_BUTTON_SELECT 109
KEYCODE_BUTTON_MODE 110
KEYCODE_ESCAPE 111 ESC键
KEYCODE_FORWARD_DEL 112 删除键
KEYCODE_CTRL_LEFT 113
KEYCODE_CTRL_RIGHT 114
KEYCODE_CAPS_LOCK 115 大写锁定键
KEYCODE_SCROLL_LOCK 116
KEYCODE_META_LEFT 117
KEYCODE_META_RIGHT 118
KEYCODE_FUNCTION 119
KEYCODE_SYSRQ 120
KEYCODE_BREAK 121 Break/Pause键
KEYCODE_MOVE_HOME 122 光标移动到开始键
KEYCODE_MOVE_END 123 光标移动到末尾键
KEYCODE_INSERT 124
KEYCODE_FORWARD 125
KEYCODE_MEDIA_PLAY 126 多媒体键 播放
KEYCODE_MEDIA_PAUSE 127 多媒体键 暂停
KEYCODE_MEDIA_CLOSE 128 多媒体键 关闭
KEYCODE_MEDIA_EJECT 129 多媒体键 弹出
KEYCODE_MEDIA_RECORD 130 多媒体键 录音
KEYCODE_F1 131
KEYCODE_F2 132
KEYCODE_F3 133
KEYCODE_F4 134
KEYCODE_F5 135
KEYCODE_F6 136
KEYCODE_F7 137
KEYCODE_F8 138
KEYCODE_F9 139
KEYCODE_F10 140
KEYCODE_F11 141
KEYCODE_F12 142
KEYCODE_NUM_LOCK 143 小键盘锁
KEYCODE_NUMPAD_0 144
KEYCODE_NUMPAD_1 145
KEYCODE_NUMPAD_2 146
KEYCODE_NUMPAD_3 147
KEYCODE_NUMPAD_4 148
KEYCODE_NUMPAD_5 149
KEYCODE_NUMPAD_6 150
KEYCODE_NUMPAD_7 151
KEYCODE_NUMPAD_8 152
KEYCODE_NUMPAD_9 153
KEYCODE_NUMPAD_DIVIDE 154
KEYCODE_NUMPAD_MULTIPLY 155
KEYCODE_NUMPAD_SUBTRACT 156
KEYCODE_NUMPAD_ADD 157
KEYCODE_NUMPAD_DOT 158
KEYCODE_NUMPAD_COMMA 159
KEYCODE_NUMPAD_ENTER 160
KEYCODE_NUMPAD_EQUALS 161
KEYCODE_NUMPAD_LEFT_PAREN 162
KEYCODE_NUMPAD_RIGHT_PAREN 163
KEYCODE_VOLUME_MUTE 164 扬声器静音键
KEYCODE_INFO 165
KEYCODE_CHANNEL_UP 166
KEYCODE_CHANNEL_DOWN 167
KEYCODE_ZOOM_IN 168 放大键
KEYCODE_ZOOM_OUT 169 缩小键
KEYCODE_TV 170
KEYCODE_WINDOW 171
KEYCODE_GUIDE 172
KEYCODE_DVR 173
KEYCODE_BOOKMARK 174
KEYCODE_CAPTIONS 175
KEYCODE_SETTINGS 176
KEYCODE_TV_POWER 177
KEYCODE_TV_INPUT 178
KEYCODE_STB_POWER 179
KEYCODE_STB_INPUT 180
KEYCODE_AVR_POWER 181
KEYCODE_AVR_INPUT 182
KEYCODE_PROG_RED 183
KEYCODE_PROG_GREEN 184
KEYCODE_PROG_YELLOW 185
KEYCODE_PROG_BLUE 186
KEYCODE_APP_SWITCH 187
KEYCODE_BUTTON_1 188
KEYCODE_BUTTON_2 189
KEYCODE_BUTTON_3 190
KEYCODE_BUTTON_4 191
KEYCODE_BUTTON_5 192
KEYCODE_BUTTON_6 193
KEYCODE_BUTTON_7 194
KEYCODE_BUTTON_8 195
KEYCODE_BUTTON_9 196
KEYCODE_BUTTON_10 197
KEYCODE_BUTTON_11 198
KEYCODE_BUTTON_12 199
KEYCODE_BUTTON_13 200
KEYCODE_BUTTON_14 201
KEYCODE_BUTTON_15 202
KEYCODE_BUTTON_16 203
KEYCODE_LANGUAGE_SWITCH 204
KEYCODE_MANNER_MODE 205
KEYCODE_3D_MODE 206
KEYCODE_CONTACTS 207
KEYCODE_CALENDAR 208
KEYCODE_MUSIC 209
KEYCODE_CALCULATOR 210
KEYCODE_ZENKAKU_HANKAKU 211
KEYCODE_EISU 212
KEYCODE_MUHENKAN 213
KEYCODE_HENKAN 214
KEYCODE_KATAKANA_HIRAGANA 215
KEYCODE_YEN 216
KEYCODE_RO 217
KEYCODE_KANA 218
KEYCODE_ASSIST 219
KEYCODE_BRIGHTNESS_DOWN 220
KEYCODE_BRIGHTNESS_UP 221
KEYCODE_MEDIA_AUDIO_TRACK 222
KEYCODE_SLEEP 223
KEYCODE_WAKEUP 224
KEYCODE_PAIRING 225
KEYCODE_MEDIA_TOP_MENU 226
KEYCODE_11 227
KEYCODE_12 228
KEYCODE_LAST_CHANNEL 229
KEYCODE_TV_DATA_SERVICE 230
KEYCODE_VOICE_ASSIST 231
KEYCODE_TV_RADIO_SERVICE 232
KEYCODE_TV_TELETEXT 233
KEYCODE_TV_NUMBER_ENTRY 234
KEYCODE_TV_TERRESTRIAL_ANALOG 235
KEYCODE_TV_TERRESTRIAL_DIGITAL 236
KEYCODE_TV_SATELLITE 237
KEYCODE_TV_SATELLITE_BS 238
KEYCODE_TV_SATELLITE_CS 239
KEYCODE_TV_SATELLITE_SERVICE 240
KEYCODE_TV_NETWORK 241
KEYCODE_TV_ANTENNA_CABLE 242
KEYCODE_TV_INPUT_HDMI_1 243
KEYCODE_TV_INPUT_HDMI_2 244
KEYCODE_TV_INPUT_HDMI_3 245
KEYCODE_TV_INPUT_HDMI_4 246
KEYCODE_TV_INPUT_COMPOSITE_1 247
KEYCODE_TV_INPUT_COMPOSITE_2 248
KEYCODE_TV_INPUT_COMPONENT_1 249
KEYCODE_TV_INPUT_COMPONENT_2 250
KEYCODE_TV_INPUT_VGA_1 251
KEYCODE_TV_AUDIO_DESCRIPTION 252
KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP 253
KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN 254
KEYCODE_TV_ZOOM_MODE 255
KEYCODE_TV_CONTENTS_MENU 256
KEYCODE_TV_MEDIA_CONTEXT_MENU 257
KEYCODE_TV_TIMER_PROGRAMMING 258
KEYCODE_HELP 259
KEYCODE_NAVIGATE_PREVIOUS 260
KEYCODE_NAVIGATE_NEXT 261
KEYCODE_NAVIGATE_IN 262
KEYCODE_NAVIGATE_OUT 263
KEYCODE_STEM_PRIMARY 264
KEYCODE_STEM_1 265
KEYCODE_STEM_2 266
KEYCODE_STEM_3 267
KEYCODE_DPAD_UP_LEFT 268
KEYCODE_DPAD_DOWN_LEFT 269
KEYCODE_DPAD_UP_RIGHT 270
KEYCODE_DPAD_DOWN_RIGHT 271
KEYCODE_MEDIA_SKIP_FORWARD 272
KEYCODE_MEDIA_SKIP_BACKWARD 273
KEYCODE_MEDIA_STEP_FORWARD 274
KEYCODE_MEDIA_STEP_BACKWARD 275
KEYCODE_SOFT_SLEEP 276
KEYCODE_CUT 277
KEYCODE_COPY 278
KEYCODE_PASTE 279
KEYCODE_SYSTEM_NAVIGATION_UP 280
KEYCODE_SYSTEM_NAVIGATION_DOWN 281
KEYCODE_SYSTEM_NAVIGATION_LEFT 282
KEYCODE_SYSTEM_NAVIGATION_RIGHT 283

以上就是Flutter TV Android端开发技巧详细教程的详细内容,更多关于Flutter TV Android端开发的资料请关注我们其它相关文章!

(0)

相关推荐

  • Flutter图片缓存管理ImageCache原理分析

    目录 引言 PaintingBinding 减少图片缓存 增大阀值 思考 引言 设计: 嗯? 这个图片点击跳转进详情再返回图片怎么变白闪一下呢?产品: 是啊是啊! 一定是个bug开发: 囧囧囧 在开发过程中, 也许你也遇到过这样一个场景. 进入一个页面后,前一个页面的图片都会闪白一下. 或者在列表中,加载很多列表项后,之前列表中的图片都需要重新加载.你有没有想过这一切的原因是什么呢? 没错! 它就是我们今天介绍的主人公 --- ImageCache 可能有些人对ImageCache还有些陌生,

  • Flutter开发技巧ListView去除水波纹方法示例

    正文 ScrollConfiguration( behavior: NoScrollBehaviorWidget(), child: ListView( ...... ...... ), ), 调用ScrollConfiguration官方类,实现behavior NoScrollBehaviorWidget用于去除水波纹的自定义Widget import 'package:flutter/material.dart'; /// 去除listview水印 /// ScrollConfigurat

  • Flutter CustomPaint绘制widget使用示例

    目录 CustomPaint 介绍 使用 CustomPaint size 的大小. isComplex willChange foregroundPainter 动画 CustomPaint 介绍 Flutter CustomPaint 提供了一个 canvas,可以在绘制阶段在上面进行绘制内容. 需要绘制时,CustomPaint 首先要求它的 painter 在当前画布上绘画,然后它绘画它的 child,在绘画完它的 child 之后,要求他的 foregroundPainter 绘画.

  • Flutter runApp GestureBinding使用介绍

    目录 GestureBinding介绍 methods 总结 GestureBinding介绍 在上一篇文章<Flutter runApp到渲染上屏>中,我们介绍了从runApp直到渲染到屏幕上.为了整体流程顺畅因此一些内容没有花过多的文笔描述,所以本章节单独陈述GestureBinding这个mixin对象. 想去了解一个类最好的方法无外乎去阅读它的注释,我们可以从它的注释中去了解它是为了做什么,做了些什么, 能够做什么. 原文 汉译 A binding for the gesture su

  • Flutter最小刷新范围探索ValueListenableBuilder使用详解

    目录 引子 ValueListenableBuilder 如何用 怎么做 不足点 引子 管理对象太多? 刷新管理太麻烦 ? Flutter刷新范围控制不好 ? 不妨看看本文 , 希望提供给你一些思路吧 ! 说起 Flutter 刷新, 你的第一印象是什么 ? setState ? 是的, 只要使用过 Flutter 的人, 第一印象都必然是 setState , 但是由于 setState 滥用的问题, 性能问题就脱颖而出. 因此产出了诸如 Fish_redux 之流, 这些框架尽可能的使用 S

  • Flutter runApp到渲染上屏分析详解

    目录 起源 分析准备 ensureInitialized scheduleAttachRootWidget scheduleWarmUpFrame 总结 起源 flutter作为一个跨平台的框架,在绘制上体现出了它跨平台的良好性能.那么,它是如何从runApp()后 绘制上屏的呢?本文将与你一起去探索这一过程. ps: 为了思维不中断, 本文仅对整体流程作分析,不会深入分析具体实现 我们运行一个flutter app ,入口一定是从runApp() 中进行的. 那么flutter 在runApp

  • Flutter RendererBinding作用源码分析

    目录 分析 PipelineOwner flushLayout flushCompositingBits flushPaint flushSemantics initRenderView scheduleInitialLayout scheduleInitialPaint 分析 RendererBinding 的作用是负责render tree 和flutter engine之间的连接. 我们在启动App的时候,首先会创建 PiplineOwner ,然后通过platformDispatcher

  • Flutter SystemChrome使用方法详解

    目录 引言 setPreferredOrientations 设置横屏或坚屏 setEnabledSystemUIMode 设置全屏显示 setSystemUIOverlayStyle 设置 overlay 样式, 全屏播放视频 setSystemUIChangeCallback AnnotatedRegion 引言 SystemChrome 控制操作系统图形界面的特定方面以及它如何与应用程序交互. 需要注意的是在使用的时候一定要保证先执行 WidgetsFlutterBinding.ensur

  • Flutter TV Android端开发技巧详细教程

    目录 前言 开发思路 先上效果 开发细节 使用RawKeyboardListener Provider层对事件进行处理 注意 总结 文件参考 TV keyCode详解 前言 最近公司有了新的业务,把现有Flutter Android项目应用到TV上去,这不,Asscre的活就来了. 本文详细说明Flutter for TV的两种实现方式,能力有限,不足之处欢迎指点,哈哈哈 开发思路 在开发之前,我们先设定一下我们的思路. 即,如何对原有程序代码侵入式最小.性能最佳.可玩性更高做出设定. 那么,通

  • Android Studio开发环境搭建教程详解

    对于移动端这块,笔者之前一直都是进行iOS开发的,也从来没用过Java.但是因为进入了Google Android全国大学生移动互联网创新挑战赛(进入官网)的总决赛(笔者"西部计算机教育提升计划"的项目被直接推荐进入决赛),这个比赛要求一定要提交apk程序,所以我不得不赶紧学习一下Android开发了. 下面就对自己学习的过程做一个记录. 一.安装Android Studio 笔者用的计算机配置如下: Mac下安装Android Studio应该更简单一些,只需要下载一个Android

  • android studio 3.4配置Android -jni 开发基础的教程详解

    首先下载配置android studio ndk 1.打开sdkManager下载CMake和LLDB 2.配置ndk 项目新建 项目建立完毕后,工程目录如下,cpp文件夹是系统自动生成的 3.自定义 navite方法 接下来开始写自定义的一个native方法,新建一个Hello.java文件,里面写一个add求和的native方法,如下 生成c++头文件 然后在windows控制台Terminal进入hello.java所在的目录执行javac hello.java,如下 执行完毕后hello

  • 30条android项目开发技巧与经验总结

    1.如果是阅读型文本(例如一篇文章),不需要固定大小的,textSize可以使用sp:如果是展示型文本(例如按钮中的文本),其大小受到限制的,textSize可以使用dp. 2.使用json用作网络数据传输时,应该使用String字段取代int字段. 3.按照现在正常密度比(系统的densityDPI根据分辨率和屏幕尺寸为正常的120.160.240.320.480.640时)9:16的安卓机其尺寸为(360dp*540dp).UI有时会根据iPhone机型使用750px*1334px作图,而按

  • jQuery常见开发技巧详细整理

    1.关于页面元素的引用 通过jquery的$()引用元素包括通过id.class.元素名以及元素的层级关系及dom或者xpath条件等方法,且返回的对象为jquery对象(集合对象),不能直接调用dom定义的方法. 2.jQuery对象与dom对象的转换 只有jquery对象才能使用jquery定义的方法.注意dom对象和jquery对象是有区别的,调用方法时要注意操作的是dom对象还是jquery对象. 普通的dom对象一般可以通过$()转换成jquery对象. 如:$(document.ge

  • 图解Windows环境下Android Studio安装和使用教程

    鉴于谷歌最新推出的Android Studio备受开发者的推崇,所以也跟着体验一下. 一.介绍Android Studio  Android Studio 是一个Android开发环境,基于IntelliJ IDEA. 类似 Eclipse ADT,Android Studio 提供了集成的 Android 开发工具用于开发和调试. 最近,Google 已宣布,为了简化 Android 的开发力度,以重点建设 Android Studio 工具,到今年年底将停止支持Eclipse等其他集成开发环

  • Android开发组件flutter的20个常用技巧示例总结

    目录 1.map遍历快速实现边距,文字自适应改变大小 2.使用SafeArea 添加边距 3.布局思路 4.获取当前屏幕的大小 5.文本溢出显示省略号 6.一个圆角带搜索icon的搜索框案例 7.修改按钮的背景色 8.tab切换实例 9.点击事件组件点击空白区域不触发点击 10.使用主题色 11.往安卓模拟器中传图片 12.控制text的最大行数显示影藏文字 13.去掉默认的抽屉图标 14.图片占满屏 15.倒计时 16.固定底部 17.添加阴影 18.隐藏键盘 19.获取父级组件大小 20.点

  • Android NDK开发详细介绍

    Android之NDK开发 一.NDK产生的背景 Android平台从诞生起,就已经支持C.C++开发.众所周知,Android的SDK基于Java实现,这意味着基于Android SDK进行开发的第三方应用都必须使用Java语言.但这并不等同于"第三方应用只能使用Java".在Android SDK首次发布时,Google就宣称其虚拟机Dalvik支持JNI编程方式,也就是第三方应用完全可以通过JNI调用自己的C动态库,即在Android平台上,"Java+C"的

  • 仅5步搞定Android开发环境部署 Android开发环境搭建教程

    在windows安装Android的开发环境不简单也说不上算复杂,本文写给第一次想在自己Windows上建立Android开发环境投入Android浪潮的朋友们,为了确保大家能顺利完成开发环境的搭建,文章写的尽量详细,希望对准备进入Android开发的朋友有帮助. 本教程将分为五个步骤来完成Android开发环境的部署. 第一步:安装JDK. 第二步:配置Windows上JDK的变量环境 . 第三步: 下载安装Eclipse . 第四步:下载安装Android SDK . 第五步:为Eclips

随机推荐