flutter 踩坑

assassin_cike浙江

1. 使用flutter_svg引入svg报错

该svg是从figma导出的。
flutter_svg 报错
翻阅 Out of order defs/references #102defs放到svg标签的顶部,可以解决这个报错,且修正了该svg内部小图标的颜色问题,从而让页面上的svg跟figma上看起来一致。

2. 使用GridView滚动的时候报错

*The following assertion was thrown while notifying status listeners for AnimationController:
The provided ScrollController is currently attached to more than one ScrollPosition.*

class GridList extends StatelessWidget {
  const GridList({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {\
    return GridView.count(
      crossAxisCount: 3,
      crossAxisSpacing: 32,
      mainAxisSpacing: 32,
      scrollDirection: Axis.vertical,
      childAspectRatio: (1 / .75),
      shrinkWrap: true,
      children: const [
        GridCard(),
        GridCard(),
        GridCard(),
        GridCard(),
        GridCard(),
        GridCard(),
      ],
    );
  }
}

GridView 滚动报错
参考Flutter错误The provided ScrollController is attached to more than one ScrollPosition需要给GridView添加个controller。

3. OutlinedButton 默认是disabled状态

OutlinedButton 默认是disabled状态,需要传递onPressed或者onLongPress才是可用状态,ButtonStyleButton做了解释

  /// Whether the button is enabled or disabled.
  ///
  /// Buttons are disabled by default. To enable a button, set its [onPressed]
  /// or [onLongPress] properties to a non-null value.
  bool get enabled => onPressed != null || onLongPress != null;

4. hydrated_bloc 持久化数据如何重新同步到客户端

需求:
不同的用户登录同一个客户端需要保留之前的操作记录(比如之前通过trtc设置的麦克风音量)。
直接上代码

class SettingBloc extends HydratedBloc<SettingEvent, SettingState> {
  final String uid;

  SettingBloc({required this.uid}) : super(SettingInitial()) {
    on<SettingVolumeUpdate>(_onVolumeUpdate);
    on<SettingInitialize>(_onSettingInitialize);
    add(const SettingInitialize()); // 初始化数据
  }

  final TRTC trtc = TRTC();

  void _onSettingInitialize(
    SettingInitialize event,
    Emitter<SettingState> emit,
  ) async {
    // 在这里可以拿到原始的state或从fromJson中返回的state
    add(SettingVolumeUpdate(state.headsetVolume, false));

    add(SettingVolumeUpdate(state.microphoneVolume, true));
  }

  void _onVolumeUpdate(
    SettingVolumeUpdate event,
    Emitter<SettingState> emit,
  ) async {
    if (event.isMicrophone) {
      await trtc.setCurrentMicDeviceVolume(event.volume.toInt()); // 设置麦克风音量
    } else {
      await trtc.setCurrentSpeakerDeviceVolume(event.volume.toInt());
    }
    emit(event.isMicrophone
        ? state.copyWith(microphoneVolume: event.volume)
        : state.copyWith(headsetVolume: event.volume));
  }

  @override
  String get id => uid; // 缓存不同用户的数据

  @override
  SettingState? fromJson(Map<String, dynamic> json) { // 从缓存中读取数据
    final headsetVolume = json['headsetVolume'];
    final microphoneVolume = json['microphoneVolume'];
    // 在这里不能直接通过add(SettingVolumeUpdate(state.microphoneVolume, true))触发事件,
    // 因为fromJson在SettingBloc构造函数之前执行,事件还没注册,
    // 但是下面返回的结果会在_onSettingInitialize中拿到
    // 从而在_onSettingInitialize中触发SettingVolumeUpdate 
    return SettingState(
      headsetVolume: headsetVolume ?? 100,
      microphoneVolume: microphoneVolume ?? 100,
    );
  }

  @override
  Map<String, dynamic>? toJson(SettingState state) { // 将bloc的数据缓存起来
    return {
      "microphoneVolume": state.microphoneVolume,
      "headsetVolume": state.headsetVolume,
    };
  }
}

5. 空容器怎么触发onSecondaryTapDown事件

class Separator extends StatelessWidget {
  Separator({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      behavior: HitTestBehavior.translucent,  // 需要加这行才能触发onSecondaryTapDown事件
      onSecondaryTapDown: (TapDownDetails details) {
      },
      child: const SizedBox( // child为空无法触发onSecondaryTapDown事件
        height: 28,
      ),
    );
  }
}

6. dropdown_button2@1.8.2 customButton 点击menu没法弹出

customButton 是我自定义的一个svg icon 用了GestureDetector注册了onTap事件

GestureDetector(
              onTapDown: (details) {
                setState(() {
                  isPressed = true;
                });
                widget.onTapDown?.call(details);
              },
              onTapUp: (details) {
                setState(() {
                  isPressed = false;
                });
              },
              onTapCancel: () {
                setState(() {
                  isPressed = false;
                });
              },
              onTap: () {
                widget.onTap?.call();
              },
              behavior: widget.behavior,
              child: SizedBox.square(
                dimension: widget.size,
                child: SvgPicture.asset(
                  svgStatusSrc,
                ),
              ),
            ),

customButton的注释:

  /// Uses custom widget like icon,image,etc.. instead of the default button
  final Widget? customButton;

我直接用SvgPicture.asset作为customButton是没问题的,所以怀疑是我的图标控件注册了onTap事件的问题,果然去掉了GestureDetector就没问题了。但是这个是通用控件,不能把GestureDetector去掉,有什么好办法去触发dropdownbutton的onTap事件呢?
Does this widget allow you to open the button programmatically?给出了答案

 final dropdownKey = GlobalKey<DropdownButton2State>();

 GestureDetector(
              onTap: () {
                dropdownKey.currentState!.callTap();
              },
              child: Container(
                height: 100,
                width: 100,
                color: Colors.red,
              )

DropdownButton2(key: dropdownKey,)

为啥图标的onTap事件会阻止dropdown_button2事件的触发,后续再探。

7. GridView 渲染固定高度的卡片列表,无法适配窗口的宽度

        return GridView.extent(
          // 可以根据窗口宽度弹性的伸缩,但是我们的item是固定的,GridView做不到。
          maxCrossAxisExtent: 293,
          // crossAxisCount: 3,
          crossAxisSpacing: 32,
          mainAxisSpacing: 32,
          scrollDirection: Axis.vertical,
          childAspectRatio: childAspectRatio,
          shrinkWrap: true,
          controller: controller,
          children: derivedAreaList
              .map((e) => AreaGridCard(
                    areaItem: e,
                    areaType: areaType,
                  ))
              .toList(),
        );

上述代码可以根据窗口大小在一行渲染不同的数量的卡片,但是卡片的高度会跟着变,因为宽高是由childAspectRatio控制,也有人吐槽这个设计.Change height of GridView row to fixed height 。先找了一个库responsive_grid: ^2.1.0但是没法做到最边上的边距为0。于是换个responsive_grid_list: ^1.3.0满足了我们的要求。

8. flutter windows端监听Ctrl V组合键

RawKeyboardListener并不能监听到组合键,经过艰难搜索LogicalKeyboardKey.meta in shortcut is triggered without pressing the other keys in the LogicalKeySet指明了方向,SingleActivator是关键,具体实践:

return CallbackShortcuts(
      bindings: {
        const SingleActivator(LogicalKeyboardKey.keyV, ctrl: true): _handlePastePressed,
      },
      child: Focus(
        autofocus: true,
        child: TextField(),
      ),
    );

参考:
Flutter: Keyboard Shortcuts, the easy way!

9. 退出页面,怎样清空存在bloc里的数据?

How to eliminate state in BlocBuilder? 讨论的结果是bloc注册在页面级别,而不是全局,这样页面销毁该bloc就会被销毁,但是我们的需求是多个页面共享该bloc,所以该bloc需要绑定到全局,具体怎么操作还没找到好的办法。

10. showDialog 无法在 Overlay 上面显示

Problem showing AlertDialog on top of Overlay有人遇到了相同的问题,但是没有得到解决。

阅读 359

前端大杂烩
前端随想,专注于react、vue技术栈

生活不是得过且过

1.3k 声望
68 粉丝
0 条评论

生活不是得过且过

1.3k 声望
68 粉丝
宣传栏