| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/rendering.dart'; |
| |
| /// Flutter code sample for [SelectionContainer]. |
| |
| void main() => runApp(const SelectionContainerExampleApp()); |
| |
| class SelectionContainerExampleApp extends StatelessWidget { |
| const SelectionContainerExampleApp({super.key}); |
| |
| @override |
| Widget build(BuildContext context) { |
| return MaterialApp( |
| home: SelectionArea( |
| child: Scaffold( |
| appBar: AppBar(title: const Text('SelectionContainer Sample')), |
| body: const Center( |
| child: SelectionAllOrNoneContainer( |
| child: Column( |
| mainAxisAlignment: MainAxisAlignment.center, |
| children: <Widget>[Text('Row 1'), Text('Row 2'), Text('Row 3')], |
| ), |
| ), |
| ), |
| ), |
| ), |
| ); |
| } |
| } |
| |
| class SelectionAllOrNoneContainer extends StatefulWidget { |
| const SelectionAllOrNoneContainer({super.key, required this.child}); |
| |
| final Widget child; |
| |
| @override |
| State<StatefulWidget> createState() => _SelectionAllOrNoneContainerState(); |
| } |
| |
| class _SelectionAllOrNoneContainerState |
| extends State<SelectionAllOrNoneContainer> { |
| final SelectAllOrNoneContainerDelegate delegate = |
| SelectAllOrNoneContainerDelegate(); |
| |
| @override |
| void dispose() { |
| delegate.dispose(); |
| super.dispose(); |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| return SelectionContainer(delegate: delegate, child: widget.child); |
| } |
| } |
| |
| class SelectAllOrNoneContainerDelegate |
| extends MultiSelectableSelectionContainerDelegate { |
| Offset? _adjustedStartEdge; |
| Offset? _adjustedEndEdge; |
| bool _isSelected = false; |
| |
| // This method is called when newly added selectable is in the current |
| // selected range. |
| @override |
| void ensureChildUpdated(Selectable selectable) { |
| if (_isSelected) { |
| dispatchSelectionEventToChild( |
| selectable, |
| const SelectAllSelectionEvent(), |
| ); |
| } |
| } |
| |
| @override |
| SelectionResult handleSelectWord(SelectWordSelectionEvent event) { |
| // Treat select word as select all. |
| return handleSelectAll(const SelectAllSelectionEvent()); |
| } |
| |
| @override |
| SelectionResult handleSelectionEdgeUpdate(SelectionEdgeUpdateEvent event) { |
| final Rect containerRect = Rect.fromLTWH( |
| 0, |
| 0, |
| containerSize.width, |
| containerSize.height, |
| ); |
| final Matrix4 globalToLocal = getTransformTo(null)..invert(); |
| final Offset localOffset = MatrixUtils.transformPoint( |
| globalToLocal, |
| event.globalPosition, |
| ); |
| final Offset adjustOffset = SelectionUtils.adjustDragOffset( |
| containerRect, |
| localOffset, |
| ); |
| if (event.type == SelectionEventType.startEdgeUpdate) { |
| _adjustedStartEdge = adjustOffset; |
| } else { |
| _adjustedEndEdge = adjustOffset; |
| } |
| // Select all content if the selection rect intercepts with the rect. |
| if (_adjustedStartEdge != null && _adjustedEndEdge != null) { |
| final Rect selectionRect = Rect.fromPoints( |
| _adjustedStartEdge!, |
| _adjustedEndEdge!, |
| ); |
| if (!selectionRect.intersect(containerRect).isEmpty) { |
| handleSelectAll(const SelectAllSelectionEvent()); |
| } else { |
| super.handleClearSelection(const ClearSelectionEvent()); |
| } |
| } else { |
| super.handleClearSelection(const ClearSelectionEvent()); |
| } |
| return SelectionUtils.getResultBasedOnRect(containerRect, localOffset); |
| } |
| |
| @override |
| SelectionResult handleClearSelection(ClearSelectionEvent event) { |
| _adjustedStartEdge = null; |
| _adjustedEndEdge = null; |
| _isSelected = false; |
| return super.handleClearSelection(event); |
| } |
| |
| @override |
| SelectionResult handleSelectAll(SelectAllSelectionEvent event) { |
| _isSelected = true; |
| return super.handleSelectAll(event); |
| } |
| } |