2017-09-14 6 views
2

Imaginez que j'ai un long texte comme ceci: HELLO THIS IS MY LONG SENTENCE. Je veux que le mot LONG ondule (éclaboussure d'encre) quand je tape dessus.Comment faire une ondulation TextSpan lorsque je la tape?

Disons que j'ai ce code:

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

Merci!

Répondre

1

example

Si vous voulez une solution générique pour placer des widgets sur des portions de texte, see this gist.

Vous pouvez utiliser le code suivant pour avoir l'ondulation contraint à une section spécifique du texte:

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

Donc, l'astuce est les décalages de TextSelection doivent correspondre au chapeau de sous-chaîne que vous voulez ridée? –

+0

@SethLadd oui exactement, il n'a même pas à suivre les étendues de texte – Takhion

2

Vous pouvez réaliser cet effet en adaptant le code ink_well.dart.

Dans cet exemple, j'ai configuré le rectCallback pour développer la carte contenant, mais vous pouvez fournir un rectangle plus petit avec le splash centré autour du point du tap.

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(), 
     ), 
     ), 
    ), 
    ); 
    } 
}