Flutter彻底去除水波纹效果,不来看看?

在使用flutter进行开发的时候,按钮等控件点击会有水波纹效果,这是MaterialApp的交互效果,但是很多从原生转到flutter开发的同学,可能很不习惯这样的效果,或者有时候不需要此效果,要怎么搞呢?

设置splashColor

最容易想到的是设置splashColor,如下:

floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
        splashColor: Colors.blue,
      )

我们只需要设置可点击widget对应的splashColor的颜色和背景色相同就可以,此时水波纹效果还在,只不过我们视觉感知不到了而已。

但是这样设置,如果是在全局ThemeData设置splashColor,我们并不知道应该设置成什么颜色,因为可点击的widget每个都可能有自己的背景颜色,而如果每个widget自己设置splashColor,又显得很繁琐,那有没有一种彻底的方法,让我不再见到这烦人的波纹呢?看官且往下看!

深入源码,斩草除根

我们知道,水波纹的效果其实是 InkWell widget提供,上面提到的设置widget的splashColor,其实就是设置InkWell的splashColor而已。

class InkWell extends InkResponse {
  const InkWell({
    Key key,
    Widget child,
    GestureTapCallback onTap,
    GestureTapCallback onDoubleTap,
    GestureLongPressCallback onLongPress,
    GestureTapDownCallback onTapDown,
	...

查看源码可以看到InkWell继承自InkResponse,查看InkResponse的_InkResponseState中的_handleTapDown方法可以看到,点击的时候开启了水波纹:

  void _handleTapDown(TapDownDetails details) {
    _startSplash(details: details);
    if (widget.onTapDown != null) {
      widget.onTapDown(details);
    }
  }

进入_startSplash()方法中,发现在这里创建了InteractiveInkFeature类型的splash

void _startSplash({TapDownDetails details, BuildContext context}) {
    assert(details != null || context != null);

    Offset globalPosition;
    if (context != null) {
      final RenderBox referenceBox = context.findRenderObject() as RenderBox;
      assert(referenceBox.hasSize, 'InkResponse must be done with layout before starting a splash.');
      globalPosition = referenceBox.localToGlobal(referenceBox.paintBounds.center);
    } else {
      globalPosition = details.globalPosition;
    }
    final InteractiveInkFeature splash = _createInkFeature(globalPosition);
    _splashes ??= HashSet<InteractiveInkFeature>();
    _splashes.add(splash);
    _currentSplash = splash;
    updateKeepAlive();
    updateHighlight(_HighlightType.pressed, value: true);
}

InteractiveInkFeature _createInkFeature(Offset globalPosition) {
    final MaterialInkController inkController = Material.of(context);
    final RenderBox referenceBox = context.findRenderObject() as RenderBox;
    final Offset position = referenceBox.globalToLocal(globalPosition);
    final Color color = widget.splashColor ?? Theme.of(context).splashColor;
    final RectCallback rectCallback = widget.containedInkWell ? widget.getRectCallback(referenceBox) : null;
    final BorderRadius borderRadius = widget.borderRadius;
    final ShapeBorder customBorder = widget.customBorder;

    InteractiveInkFeature splash;
    void onRemoved() {
      if (_splashes != null) {
        assert(_splashes.contains(splash));
        _splashes.remove(splash);
        if (_currentSplash == splash)
          _currentSplash = null;
        updateKeepAlive();
      } // else we're probably in deactivate()
    }

    splash = (widget.splashFactory ?? Theme.of(context).splashFactory).create(
      controller: inkController,
      referenceBox: referenceBox,
      position: position,
      color: color,
      containedInkWell: widget.containedInkWell,
      rectCallback: rectCallback,
      radius: widget.radius,
      borderRadius: borderRadius,
      customBorder: customBorder,
      onRemoved: onRemoved,
      textDirection: Directionality.of(context),
    );

    return splash;
  }

InteractiveInkFeature是一个抽象类,猜测应该是水波纹的实现。

abstract class InteractiveInkFeature extends InkFeature {
  /// Creates an InteractiveInkFeature.
  ///
  /// The [controller] and [referenceBox] arguments must not be null.
  InteractiveInkFeature({
    @required MaterialInkController controller,
    @required RenderBox referenceBox,
    Color color,
    VoidCallback onRemoved,
  }) : assert(controller != null),
       assert(referenceBox != null),
       _color = color,
       super(controller: controller, referenceBox: referenceBox, onRemoved: onRemoved);
       ...

可以看到InkResponse通过自身属性(widget.splashFactory)或者全局Theme(Theme.of(context).splashFactory),调用create方法创建splash对象。既然既然直接使用Theme中的splashFactory会有波纹效果,那我们可不可以自己实现一个不带波纹的splashFactory呢?话不多说,直接上手搞一搞!

创建一个新的dart文件no_splash.dart,创建NoSplashFactory类继承自InteractiveInkFeatureFactory,在create方法里return一个空的实现:

import 'package:flutter/material.dart';

class NoSplashFactory extends InteractiveInkFeatureFactory {
  InteractiveInkFeature create({MaterialInkController controller, RenderBox referenceBox, Offset position, Color color, TextDirection textDirection, bool containedInkWell = false, rectCallback, BorderRadius borderRadius, ShapeBorder customBorder, double radius, onRemoved}) {
    return _NoInteractiveInkFeature(controller: controller, referenceBox: referenceBox);
  }
}

class _NoInteractiveInkFeature extends InteractiveInkFeature {
  _NoInteractiveInkFeature({
     MaterialInkController controller,
     RenderBox referenceBox,
  }) : super(controller: controller, referenceBox: referenceBox);

  void paintFeature(Canvas canvas, Matrix4 transform) {}
}

这样就准备好了一个不带水波纹效果的splashFactory,接下来在全局的Theme中设置splashFactory:

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        splashFactory: NoSplashFactory(),
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

好了,一切准备就绪,迫不及待的运行起来,迫不及待的点点button,不出意外的水波纹效果没有了,perfect!

你可能感兴趣的:(flutter,flutter,水波纹,源码,ios,android)