点击它时如何制作TextSpan波纹?

时间:2017-09-14 16:38:42

标签: dart flutter

想象一下,我有一段很长的文字:HELLO THIS IS MY LONG SENTENCE。当我点击它时,我想要LONG这个词涟漪(墨水溅)。

假设我有这段代码:

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'),
    ],
  ),
)

谢谢!

2 个答案:

答案 0 :(得分:5)

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;
}

答案 1 :(得分:3)

您可以通过调整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(),
          ),
        ),
      ),
    );
  }
}