focus_test.dart 6.84 KB
Newer Older
Hixie's avatar
Hixie committed
1 2 3 4
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

Adam Barth's avatar
Adam Barth committed
5
import 'package:flutter_test/flutter_test.dart';
6
import 'package:flutter/widgets.dart';
Hixie's avatar
Hixie committed
7

8
class TestFocusable extends StatefulWidget {
9
  const TestFocusable({
10
    Key key,
11 12
    this.no,
    this.yes,
13
    this.autofocus: true,
14 15
  }) : super(key: key);

Hixie's avatar
Hixie committed
16 17
  final String no;
  final String yes;
18 19
  final bool autofocus;

20 21 22 23 24 25 26 27 28 29 30
  @override
  TestFocusableState createState() => new TestFocusableState();
}

class TestFocusableState extends State<TestFocusable> {
  final FocusNode focusNode = new FocusNode();
  bool _didAutofocus = false;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
31
    if (!_didAutofocus && widget.autofocus) {
32 33 34 35 36 37 38 39 40 41 42
      _didAutofocus = true;
      FocusScope.of(context).autofocus(focusNode);
    }
  }

  @override
  void dispose() {
    focusNode.dispose();
    super.dispose();
  }

43
  @override
Hixie's avatar
Hixie committed
44 45
  Widget build(BuildContext context) {
    return new GestureDetector(
46 47 48 49
      onTap: () { FocusScope.of(context).requestFocus(focusNode); },
      child: new AnimatedBuilder(
        animation: focusNode,
        builder: (BuildContext context, Widget child) {
Ian Hickson's avatar
Ian Hickson committed
50
          return new Text(focusNode.hasFocus ? widget.yes : widget.no, textDirection: TextDirection.ltr);
51 52
        },
      ),
Hixie's avatar
Hixie committed
53 54 55 56 57
    );
  }
}

void main() {
58 59
  testWidgets('Can have multiple focused children and they update accordingly', (WidgetTester tester) async {
    await tester.pumpWidget(
60
      new Column(
61
        children: const <Widget>[
62
          const TestFocusable(
63 64 65
            no: 'a',
            yes: 'A FOCUSED',
          ),
66
          const TestFocusable(
67 68 69 70 71
            no: 'b',
            yes: 'B FOCUSED',
          ),
        ],
      ),
72
    );
73 74 75 76

    // Autofocus is delayed one frame.
    await tester.pump();

77 78 79 80
    expect(find.text('a'), findsNothing);
    expect(find.text('A FOCUSED'), findsOneWidget);
    expect(find.text('b'), findsOneWidget);
    expect(find.text('B FOCUSED'), findsNothing);
81
    await tester.tap(find.text('A FOCUSED'));
82
    await tester.idle();
83
    await tester.pump();
84 85 86 87
    expect(find.text('a'), findsNothing);
    expect(find.text('A FOCUSED'), findsOneWidget);
    expect(find.text('b'), findsOneWidget);
    expect(find.text('B FOCUSED'), findsNothing);
88
    await tester.tap(find.text('A FOCUSED'));
89
    await tester.idle();
90
    await tester.pump();
91 92 93 94
    expect(find.text('a'), findsNothing);
    expect(find.text('A FOCUSED'), findsOneWidget);
    expect(find.text('b'), findsOneWidget);
    expect(find.text('B FOCUSED'), findsNothing);
95
    await tester.tap(find.text('b'));
96
    await tester.idle();
97
    await tester.pump();
98 99 100 101
    expect(find.text('a'), findsOneWidget);
    expect(find.text('A FOCUSED'), findsNothing);
    expect(find.text('b'), findsNothing);
    expect(find.text('B FOCUSED'), findsOneWidget);
102
    await tester.tap(find.text('a'));
103
    await tester.idle();
104
    await tester.pump();
105 106 107 108
    expect(find.text('a'), findsNothing);
    expect(find.text('A FOCUSED'), findsOneWidget);
    expect(find.text('b'), findsOneWidget);
    expect(find.text('B FOCUSED'), findsNothing);
Hixie's avatar
Hixie committed
109
  });
110

111 112
  testWidgets('Can blur', (WidgetTester tester) async {
    await tester.pumpWidget(
113
      const TestFocusable(
114 115 116 117
        no: 'a',
        yes: 'A FOCUSED',
        autofocus: false,
      ),
118
    );
119

120 121
    expect(find.text('a'), findsOneWidget);
    expect(find.text('A FOCUSED'), findsNothing);
122

123 124 125
    final TestFocusableState state = tester.state(find.byType(TestFocusable));
    FocusScope.of(state.context).requestFocus(state.focusNode);
    await tester.idle();
126
    await tester.pump();
127

128 129
    expect(find.text('a'), findsNothing);
    expect(find.text('A FOCUSED'), findsOneWidget);
130

131 132
    state.focusNode.unfocus();
    await tester.idle();
133
    await tester.pump();
134

135 136
    expect(find.text('a'), findsOneWidget);
    expect(find.text('A FOCUSED'), findsNothing);
137
  });
138

139
  testWidgets('Can move focus to scope', (WidgetTester tester) async {
140 141 142
    final FocusScopeNode parentFocusScope = new FocusScopeNode();
    final FocusScopeNode childFocusScope = new FocusScopeNode();

143
    await tester.pumpWidget(
144 145 146
      new FocusScope(
        node: parentFocusScope,
        autofocus: true,
147
        child: new Row(
148
          textDirection: TextDirection.ltr,
149
          children: const <Widget>[
150
            const TestFocusable(
151 152
              no: 'a',
              yes: 'A FOCUSED',
153 154 155 156 157
              autofocus: false,
            ),
          ],
        ),
      ),
158 159 160 161 162
    );

    expect(find.text('a'), findsOneWidget);
    expect(find.text('A FOCUSED'), findsNothing);

163 164 165
    final TestFocusableState state = tester.state(find.byType(TestFocusable));
    FocusScope.of(state.context).requestFocus(state.focusNode);
    await tester.idle();
166
    await tester.pump();
167 168 169 170

    expect(find.text('a'), findsNothing);
    expect(find.text('A FOCUSED'), findsOneWidget);

171 172
    expect(parentFocusScope, hasAGoodToStringDeep);
    expect(
173
      parentFocusScope.toStringDeep(minLevel: DiagnosticLevel.info),
174 175 176 177 178 179 180 181
      equalsIgnoringHashCodes(
          'FocusScopeNode#00000\n'
          '   focus: FocusNode#00000(FOCUSED)\n'
      ),
    );

    expect(WidgetsBinding.instance.focusManager.rootScope, hasAGoodToStringDeep);
    expect(
182
      WidgetsBinding.instance.focusManager.rootScope.toStringDeep(minLevel: DiagnosticLevel.info),
183 184 185 186 187 188 189
      equalsIgnoringHashCodes(
        'FocusScopeNode#00000\n'
        ' └─child 1: FocusScopeNode#00000\n'
        '     focus: FocusNode#00000(FOCUSED)\n'
      ),
    );

190 191
    parentFocusScope.setFirstFocus(childFocusScope);
    await tester.idle();
192

193
    await tester.pumpWidget(
194 195
      new FocusScope(
        node: parentFocusScope,
196
        child: new Row(
197
          textDirection: TextDirection.ltr,
198
          children: <Widget>[
199
            const TestFocusable(
200 201
              no: 'a',
              yes: 'A FOCUSED',
202
              autofocus: false,
203
            ),
204 205
            new FocusScope(
              node: childFocusScope,
206 207
              child: new Container(
                width: 50.0,
208 209 210 211 212 213
                height: 50.0,
              ),
            ),
          ],
        ),
      ),
214 215 216 217 218
    );

    expect(find.text('a'), findsOneWidget);
    expect(find.text('A FOCUSED'), findsNothing);

219
    await tester.pumpWidget(
220 221
      new FocusScope(
        node: parentFocusScope,
222
        child: new Row(
223
          textDirection: TextDirection.ltr,
224
          children: const <Widget>[
225
            const TestFocusable(
226 227
              no: 'a',
              yes: 'A FOCUSED',
228 229 230 231 232
              autofocus: false,
            ),
          ],
        ),
      ),
233
    );
234

235 236 237
    // Focus has received the removal notification but we haven't rebuilt yet.
    expect(find.text('a'), findsOneWidget);
    expect(find.text('A FOCUSED'), findsNothing);
238

239
    await tester.pump();
240

241 242
    expect(find.text('a'), findsNothing);
    expect(find.text('A FOCUSED'), findsOneWidget);
243 244

    parentFocusScope.detach();
245
  });
Hixie's avatar
Hixie committed
246
}