// 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'; import 'package:flutter_test/flutter_test.dart'; void main() { Future pumpContainer(WidgetTester tester, Widget child) async { await tester.pumpWidget(MaterialApp(home: child)); } testWidgets('updates its registrar and delegate based on the number of selectables', ( WidgetTester tester, ) async { final registrar = TestSelectionRegistrar(); final delegate = TestContainerDelegate(); addTearDown(delegate.dispose); await pumpContainer( tester, SelectionContainer( registrar: registrar, delegate: delegate, child: const Column( children: [ Text('column1', textDirection: TextDirection.ltr), Text('column2', textDirection: TextDirection.ltr), Text('column3', textDirection: TextDirection.ltr), ], ), ), ); await tester.pumpAndSettle(); expect(registrar.selectables.length, 1); expect(delegate.selectables.length, 3); }); testWidgets('disabled container', (WidgetTester tester) async { final registrar = TestSelectionRegistrar(); final delegate = TestContainerDelegate(); addTearDown(delegate.dispose); await pumpContainer( tester, SelectionContainer( registrar: registrar, delegate: delegate, child: const SelectionContainer.disabled( child: Column( children: [ Text('column1', textDirection: TextDirection.ltr), Text('column2', textDirection: TextDirection.ltr), Text('column3', textDirection: TextDirection.ltr), ], ), ), ), ); expect(registrar.selectables.length, 0); expect(delegate.selectables.length, 0); }); testWidgets('Swapping out container delegate does not crash', (WidgetTester tester) async { final registrar = TestSelectionRegistrar(); final delegate = TestContainerDelegate(); addTearDown(delegate.dispose); final childDelegate = TestContainerDelegate(); addTearDown(childDelegate.dispose); await pumpContainer( tester, SelectionContainer( registrar: registrar, delegate: delegate, child: Builder( builder: (BuildContext context) { return SelectionContainer( registrar: SelectionContainer.maybeOf(context), delegate: childDelegate, child: const Text('dummy'), ); }, ), ), ); await tester.pumpAndSettle(); expect(registrar.selectables.length, 1); expect(delegate.value.hasContent, isTrue); final newDelegate = TestContainerDelegate(); addTearDown(newDelegate.dispose); await pumpContainer( tester, SelectionContainer( registrar: registrar, delegate: delegate, child: Builder( builder: (BuildContext context) { return SelectionContainer( registrar: SelectionContainer.maybeOf(context), delegate: newDelegate, child: const Text('dummy'), ); }, ), ), ); await tester.pumpAndSettle(); expect(registrar.selectables.length, 1); expect(delegate.value.hasContent, isTrue); expect(tester.takeException(), isNull); }); testWidgets('Can update within one frame', (WidgetTester tester) async { final registrar = TestSelectionRegistrar(); final delegate = TestContainerDelegate(); addTearDown(delegate.dispose); final childDelegate = TestContainerDelegate(); addTearDown(childDelegate.dispose); await pumpContainer( tester, SelectionContainer( registrar: registrar, delegate: delegate, child: Builder( builder: (BuildContext context) { return SelectionContainer( registrar: SelectionContainer.maybeOf(context), delegate: childDelegate, child: const Text('dummy'), // The [Text] widget has an internal [SelectionContainer]. ); }, ), ), ); await tester.pump(); // Should finish update after flushing the micro tasks. await tester.idle(); expect(registrar.selectables.length, 1); expect(delegate.value.hasContent, isTrue); }); testWidgets('selection container registers itself if there is a selectable child', ( WidgetTester tester, ) async { final registrar = TestSelectionRegistrar(); final delegate = TestContainerDelegate(); addTearDown(delegate.dispose); await pumpContainer( tester, SelectionContainer(registrar: registrar, delegate: delegate, child: const Column()), ); expect(registrar.selectables.length, 0); await pumpContainer( tester, SelectionContainer( registrar: registrar, delegate: delegate, child: const Column(children: [Text('column1', textDirection: TextDirection.ltr)]), ), ); await tester.pumpAndSettle(); expect(registrar.selectables.length, 1); await pumpContainer( tester, SelectionContainer(registrar: registrar, delegate: delegate, child: const Column()), ); await tester.pumpAndSettle(); expect(registrar.selectables.length, 0); }); testWidgets('selection container gets registrar from context if not provided', ( WidgetTester tester, ) async { final registrar = TestSelectionRegistrar(); final delegate = TestContainerDelegate(); addTearDown(delegate.dispose); await pumpContainer( tester, SelectionRegistrarScope( registrar: registrar, child: SelectionContainer( delegate: delegate, child: const Column( children: [Text('column1', textDirection: TextDirection.ltr)], ), ), ), ); await tester.pumpAndSettle(); expect(registrar.selectables.length, 1); }); } class TestContainerDelegate extends MultiSelectableSelectionContainerDelegate { @override SelectionResult dispatchSelectionEventToChild(Selectable selectable, SelectionEvent event) { throw UnimplementedError(); } @override void ensureChildUpdated(Selectable selectable) { throw UnimplementedError(); } } class TestSelectionRegistrar extends SelectionRegistrar { final Set selectables = {}; @override void add(Selectable selectable) => selectables.add(selectable); @override void remove(Selectable selectable) => selectables.remove(selectable); }