当我点击它时,如何产生TextSpan涟漪?

问题描述:

想象一下,我有这样一段文字:HELLO THIS IS MY LONG SENTENCE。当我点击它时,我希望字LONG涟漪(墨水飞溅)。当我点击它时,如何产生TextSpan涟漪?

比方说,我有这样的代码:

new RichText(
    text: new TextSpan(
    text: 'HELLO THIS IS MY ', 
    style: DefaultTextStyle.of(context).style, 
    children: <TextSpan>[ 
     new TextSpan(text: 'LONG', style: new TextStyle(fontWeight: FontWeight.bold)), 
     new TextSpan(text: ' SENTENCE'), 
    ], 
), 
) 

谢谢!

example

如果你想有一个通用的解决方案来放置小部件对文本,see this gist的部分。

您可以使用下面的代码有限制的文本的特定部分的纹波:

import 'package:flutter/material.dart'; 
import 'package:flutter/rendering.dart'; 

import 'dart:ui' show TextBox; 
import 'dart:math'; 

void main() { 
    runApp(new MaterialApp(
    home: new Material(
     child: new Center(
     child: new Demo(), 
    ), 
    ), 
)); 
} 

class Demo extends StatelessWidget { 
    final TextSelection textSelection = 
     const TextSelection(baseOffset: 17, extentOffset: 21); 

    final GlobalKey _textKey = new GlobalKey(); 

    @override 
    Widget build(context) => new Stack(
     children: <Widget>[ 
      new RichText(
      key: _textKey, 
      text: new TextSpan(
       text: 'HELLO THIS IS MY ', 
       style: DefaultTextStyle.of(context).style, 
       children: <TextSpan>[ 
       new TextSpan(
        text: 'LONG', 
        style: new TextStyle(fontWeight: FontWeight.bold)), 
       new TextSpan(text: ' SENTENCE'), 
       ], 
      ), 
     ), 
      new Positioned.fill(
      child: new LayoutBuilder(
       builder: (context, _) => new Stack(
        children: <Widget>[ 
         new Positioned.fromRect(
         rect: _getSelectionRect(), 
         child: new InkWell(
          onTap:() => {}, // needed to show the ripple 
         ), 
        ), 
        ], 
       ), 
      ), 
     ), 
     ], 
    ); 

    Rect _getSelectionRect() => 
     (_textKey.currentContext.findRenderObject() as RenderParagraph) 
      .getBoxesForSelection(textSelection) 
      .fold(
      null, 
      (Rect previous, TextBox textBox) => new Rect.fromLTRB(
        min(previous?.left ?? textBox.left, textBox.left), 
        min(previous?.top ?? textBox.top, textBox.top), 
        max(previous?.right ?? textBox.right, textBox.right), 
        max(previous?.bottom ?? textBox.bottom, textBox.bottom), 
       ), 
     ) ?? 
     Rect.zero; 
} 
+0

所以,关键是TextSelection偏移量需要匹配要皱子的帽子? –

+0

@SethLadd是的,它甚至不必遵循文本跨度 – Takhion

您可以通过在ink_well.dart适应的代码实现这种效果。

在此示例中,我将rectCallback配置为扩展到包含卡,但您可以提供一个更小的矩形,其中的水滴以水龙头为中心。

video

import 'package:flutter/material.dart'; 
import 'package:flutter/gestures.dart'; 
import 'dart:collection'; 

void main() { 
    runApp(new MaterialApp(home: new DemoApp())); 
} 

class DemoText extends StatefulWidget { 
    @override 
    DemoTextState createState() => new DemoTextState(); 
} 

class DemoTextState<T extends InkResponse> extends State<T> 
    with AutomaticKeepAliveClientMixin { 
    Set<InkSplash> _splashes; 
    InkSplash _currentSplash; 

    @override 
    bool get wantKeepAlive => (_splashes != null && _splashes.isNotEmpty); 

    void _handleTapDown(TapDownDetails details) { 
    final RenderBox referenceBox = context.findRenderObject(); 
    InkSplash splash; 
    splash = new InkSplash(
     controller: Material.of(context), 
     referenceBox: referenceBox, 
     containedInkWell: true, 
     rectCallback:() => referenceBox.paintBounds, 
     position: referenceBox.globalToLocal(details.globalPosition), 
     color: Theme.of(context).splashColor, 
     onRemoved:() { 
      if (_splashes != null) { 
      assert(_splashes.contains(splash)); 
      _splashes.remove(splash); 
      if (_currentSplash == splash) _currentSplash = null; 
      updateKeepAlive(); 
      } // else we're probably in deactivate() 
     }); 
    _splashes ??= new HashSet<InkSplash>(); 
    _splashes.add(splash); 
    _currentSplash = splash; 
    updateKeepAlive(); 
    } 

    void _handleTap(BuildContext context) { 
    _currentSplash?.confirm(); 
    _currentSplash = null; 
    Feedback.forTap(context); 
    } 

    void _handleTapCancel() { 
    _currentSplash?.cancel(); 
    _currentSplash = null; 
    } 

    @override 
    void deactivate() { 
    if (_splashes != null) { 
     final Set<InkSplash> splashes = _splashes; 
     _splashes = null; 
     for (InkSplash splash in splashes) splash.dispose(); 
     _currentSplash = null; 
    } 
    assert(_currentSplash == null); 
    super.deactivate(); 
    } 

    Widget build(BuildContext context) { 
    return new Padding(
     padding: new EdgeInsets.all(20.0), 
     child: new RichText(
     text: new TextSpan(
      text: 'HELLO THIS IS MY ', 
      style: DefaultTextStyle.of(context).style, 
      children: <TextSpan>[ 
      new TextSpan(
       recognizer: new TapGestureRecognizer() 
       ..onTapCancel = _handleTapCancel 
       ..onTapDown = _handleTapDown 
       ..onTap =() => _handleTap(context), 
       text: 'LONG', 
       style: new TextStyle(fontWeight: FontWeight.bold), 
      ), 
      new TextSpan(text: ' SENTENCE'), 
      ], 
     ), 
    ), 
    ); 
    } 
} 

class DemoApp extends StatelessWidget { 
    @override 
    Widget build(BuildContext context) { 
    return new Scaffold(
     body: new Center(
     child: new Container(
      height: 150.0, 
      width: 150.0, 
      child: new Card(
      child: new DemoText(), 
     ), 
     ), 
    ), 
    ); 
    } 
}