在使用flutter进行开发的时候,按钮等控件点击会有水波纹效果,这是MaterialApp的交互效果,但是很多从原生转到flutter开发的同学,可能很不习惯这样的效果,或者有时候不需要此效果,要怎么搞呢?
最容易想到的是设置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!