selectable 0.2.0 copy "selectable: ^0.2.0" to clipboard
selectable: ^0.2.0 copied to clipboard

outdated

A Flutter widget that enables text selection over all the text widgets it contains.

example/lib/main.dart

import 'dart:async';
import 'dart:math' as math;

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

import 'package:selectable/selectable.dart';

// ignore_for_file: prefer_mixin, avoid_print, prefer_const_constructors
// ignore_for_file: unused_element

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _scrollController = ScrollController();
  final _selectionController = SelectableController();
  var _isTextSelected = false;
  late Timer _timer;

  @override
  void initState() {
    super.initState();
    _selectionController.addListener(() {
      if (_isTextSelected !=
          _selectionController.getSelection()!.isTextSelected) {
        _isTextSelected = _selectionController.getSelection()!.isTextSelected;
        // print(_isTextSelected ? 'Text is selected' : 'Text is not selected');
        if (mounted) setState(() {});
      }
      // if (_selectionController.rects != null) {
      //   print('selection rects: ${_selectionController.rects}');
      // }
    });

    _timer = Timer.periodic(const Duration(seconds: 1), (_) {
      final text = _selectionController.getContainedText();
      if (text.isNotEmpty) {
        final i = random(max: text.length);
        if (_selectionController.selectWordAtIndex(i, key: 1)) {
          print('selected word at $i');
        } else {
          print('failed to select word at $i');
        }
      }
    });
  }

  @override
  void dispose() {
    _timer.cancel();
    _selectionController.dispose();
    _scrollController.dispose();
    super.dispose();
  }

  var _showSelection = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Selectable Example'),
      ),
      body: SingleChildScrollView(
        controller: _scrollController,
        child: Selectable(
          selectionController: _selectionController,
          scrollController: _scrollController,
          // selectionColor: Colors.orange.withAlpha(75),
          // showSelection: _showSelection,
          selectWordOnDoubleTap: true,
          showPopup: true,
          popupMenuItems: [
            SelectableMenuItem(type: SelectableMenuItemType.copy),
            SelectableMenuItem(
              title: 'Foo! :)',
              isEnabled: (controller) {
                // print('SelectableMenuItem Foo, isEnabled, selected text:
                // ${controller!.text}');
                return controller!.getSelection()!.isTextSelected;
              },
              handler: (controller) {
                showDialog<void>(
                  context: context,
                  barrierDismissible: true,
                  builder: (builder) {
                    return AlertDialog(
                      contentPadding: EdgeInsets.zero,
                      content: Container(
                        padding: EdgeInsets.all(16),
                        child: Text(controller!.getSelection()!.text!),
                      ),
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(8)),
                    );
                  },
                );
                return true;
              },
            ),
          ],
          child: Container(
            padding: const EdgeInsets.all(20),
            child: Column(
              //mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'Double-tap or long press on a word to select it, then drag '
                  'the selection controls to change the selection.',
                  style: Theme.of(context).textTheme.headline5,
                ),
                FloatColumn(
                  children: [
                    const SizedBox(height: 16),
                    Floatable(
                        float: FCFloat.end,
                        clear: FCClear.both,
                        padding: EdgeInsetsDirectional.only(start: 16),
                        maxWidthPercentage: 0.333,
                        child: Container(height: 150, color: Colors.orange)),
                    WrappableText(
                      text: TextSpan(
                        text: 'Indent Example',
                        style: Theme.of(context).textTheme.headline4,
                      ),
                    ),
                    const WrappableText(
                      indent: 40,
                      text: TextSpan(text: text1, style: textStyle2),
                      textAlign: TextAlign.justify,
                    ),
                    const SizedBox(height: 16),
                    Floatable(
                        float: FCFloat.start,
                        clear: FCClear.both,
                        padding: EdgeInsetsDirectional.only(end: 16, top: 8),
                        maxWidthPercentage: 0.333,
                        child: Container(height: 150, color: Colors.blue)),
                    WrappableText(
                      text: TextSpan(
                        text: 'Hanging Indent Example',
                        style: Theme.of(context).textTheme.headline4,
                      ),
                    ),
                    WrappableText(
                      indent: -40,
                      padding: const EdgeInsets.only(left: 40),
                      text:
                          const TextSpan(children: [_span], style: textStyle1),
                    ),
                  ],
                ),
                const SizedBox(height: 16),
                Text.rich(
                  _span,
                  style: textStyle2,
                  //style: Theme.of(context).textTheme.display1,
                ),
                Text('\n\n\n'),
              ],
            ),
          ),
        ),
      ),
      floatingActionButton: _isTextSelected
          ? FloatingActionButton.extended(
              onPressed: () {
                //Provider.of<Counter>(context, listen: false).increment();

                setState(() {
                  _showSelection = !_showSelection;
                  if (_showSelection) {
                    _selectionController.unhide();
                  } else {
                    _selectionController.hide();
                  }
                });

                // if (_selectionController.getSelection()!.isTextSelected) {
                //   _selectionController.deselectAll();
                // }
              },
              //tooltip: 'Increment',
              //child: Icon(Icons.add),
              label: Text(_showSelection ? 'hide selection' : 'show selection'),
            )
          : null,
    );
  }
}

// cspell: disable
const text1 = 'Lorem ipsum dolor sit amet, consectetur—adipiscing elit, sed '
    'do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim '
    'ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut '
    'aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit '
    'in voluptate velit esse cillum dolore eu fugiat nulla pariatur. '
    'Excepteur sint occaecat cupidatat non proident, sunt in culpa qui '
    'officia deserunt mollit anim id est laborum.';
const text2 = 'onsectetur adipiscing elit, sed do eiusmod tempor incididunt '
    'ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud '
    'exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. '
    'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum '
    'dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non '
    'proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';

const _span = TaggedTextSpan(
  tag: 'tag',
  children: <TextSpan>[
    TextSpan(style: TextStyle(color: Colors.red), text: 'Lor'),
    TextSpan(style: TextStyle(color: Colors.blue), text: 'em i'),
    TextSpan(style: TextStyle(color: Colors.green), text: 'psu'),
    TextSpan(style: TextStyle(color: Colors.red), text: 'm do'),
    TextSpan(style: TextStyle(color: Colors.blue), text: 'lor'),
    TextSpan(style: TextStyle(color: Colors.green), text: ' sit '),
    TextSpan(style: TextStyle(color: Colors.red), text: 'ame'),
    TextSpan(style: TextStyle(color: Colors.blue), text: 't, c'),
    TextSpan(text: text2),
  ],
);

const TextStyle textStyle1 = TextStyle(
  fontSize: 20,
  fontWeight: FontWeight.normal,
  height: 1.5,
);

const TextStyle textStyle2 = TextStyle(
  fontSize: 20,
  fontWeight: FontWeight.normal,
  height: 1.5,
  backgroundColor: Colors.transparent,
);

math.Random? _random;
const maxRandomInt = 0x100000000;

int random({int min = 0, required int max}) {
  assert(max > min);
  final safeRange = math.min(maxRandomInt, math.max(0, max - min));
  _random ??= math.Random();
  return _random!.nextInt(safeRange) + min;
}
69
likes
0
pub points
92%
popularity

Publisher

verified publisherronbooth.com

A Flutter widget that enables text selection over all the text widgets it contains.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

characters, collection, equatable, float_column, flutter, url_launcher

More

Packages that depend on selectable