icon_button_test.dart 9.43 KB
Newer Older
1 2 3 4
// Copyright 2016 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.

5 6
import 'dart:ui';

7
import 'package:flutter/material.dart';
8
import 'package:flutter/rendering.dart';
9 10
import 'package:flutter_test/flutter_test.dart';

11
import '../rendering/mock_canvas.dart';
12
import '../widgets/semantics_tester.dart';
13

14 15 16 17 18 19 20 21
class MockOnPressedFunction implements Function {
  int called = 0;

  void call() {
    called++;
  }
}

22
void main() {
23 24 25
  MockOnPressedFunction mockOnPressedFunction;

  setUp(() {
26
    mockOnPressedFunction = MockOnPressedFunction();
27 28 29 30
  });

  testWidgets('test default icon buttons are sized up to 48', (WidgetTester tester) async {
    await tester.pumpWidget(
Ian Hickson's avatar
Ian Hickson committed
31
      wrap(
32
          child: IconButton(
33
            onPressed: mockOnPressedFunction,
34
            icon: const Icon(Icons.link),
35 36 37 38
          ),
      ),
    );

39
    final RenderBox iconButton = tester.renderObject(find.byType(IconButton));
40
    expect(iconButton.size, const Size(48.0, 48.0));
41

42 43 44 45 46 47
    await tester.tap(find.byType(IconButton));
    expect(mockOnPressedFunction.called, 1);
  });

  testWidgets('test small icons are sized up to 48dp', (WidgetTester tester) async {
    await tester.pumpWidget(
Ian Hickson's avatar
Ian Hickson committed
48
      wrap(
49
          child: IconButton(
50 51
            iconSize: 10.0,
            onPressed: mockOnPressedFunction,
52
            icon: const Icon(Icons.link),
53 54 55 56
          ),
      ),
    );

57
    final RenderBox iconButton = tester.renderObject(find.byType(IconButton));
58
    expect(iconButton.size, const Size(48.0, 48.0));
59 60 61 62
  });

  testWidgets('test icons can be small when total size is >48dp', (WidgetTester tester) async {
    await tester.pumpWidget(
Ian Hickson's avatar
Ian Hickson committed
63
      wrap(
64
          child: IconButton(
65
            iconSize: 10.0,
66
            padding: const EdgeInsets.all(30.0),
67
            onPressed: mockOnPressedFunction,
68
            icon: const Icon(Icons.link),
69 70 71 72
          ),
      ),
    );

73
    final RenderBox iconButton = tester.renderObject(find.byType(IconButton));
74
    expect(iconButton.size, const Size(70.0, 70.0));
75 76 77
  });

  testWidgets('test default icon buttons are constrained', (WidgetTester tester) async {
78
    await tester.pumpWidget(
Ian Hickson's avatar
Ian Hickson committed
79
      wrap(
80
          child: IconButton(
81
            padding: EdgeInsets.zero,
82
            onPressed: mockOnPressedFunction,
83
            icon: const Icon(Icons.ac_unit),
84 85 86 87 88
            iconSize: 80.0,
          ),
      ),
    );

89
    final RenderBox box = tester.renderObject(find.byType(IconButton));
90
    expect(box.size, const Size(80.0, 80.0));
91 92
  });

93
  testWidgets('test default icon buttons can be stretched if specified', (WidgetTester tester) async {
94
    await tester.pumpWidget(
95
      Directionality(
96
        textDirection: TextDirection.ltr,
97 98
        child: Material(
          child: Row(
99 100
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget> [
101
              IconButton(
102 103 104 105 106
                onPressed: mockOnPressedFunction,
                icon: const Icon(Icons.ac_unit),
              ),
            ],
          ),
107 108 109 110
        ),
      ),
    );

111
    final RenderBox box = tester.renderObject(find.byType(IconButton));
112
    expect(box.size, const Size(48.0, 600.0));
113 114 115 116
  });

  testWidgets('test default padding', (WidgetTester tester) async {
    await tester.pumpWidget(
Ian Hickson's avatar
Ian Hickson committed
117
      wrap(
118
          child: IconButton(
119
            onPressed: mockOnPressedFunction,
120
            icon: const Icon(Icons.ac_unit),
121 122 123
            iconSize: 80.0,
          ),
      ),
124 125
    );

126
    final RenderBox box = tester.renderObject(find.byType(IconButton));
127
    expect(box.size, const Size(96.0, 96.0));
128 129 130 131
  });

  testWidgets('test tooltip', (WidgetTester tester) async {
    await tester.pumpWidget(
132 133 134 135
      MaterialApp(
        home: Material(
          child: Center(
            child: IconButton(
136
              onPressed: mockOnPressedFunction,
137
              icon: const Icon(Icons.ac_unit),
138 139 140 141 142 143 144 145 146
            ),
          ),
        ),
      ),
    );

    expect(find.byType(Tooltip), findsNothing);

    // Clear the widget tree.
147
    await tester.pumpWidget(Container(key: UniqueKey()));
148 149

    await tester.pumpWidget(
150 151 152 153
      MaterialApp(
        home: Material(
          child: Center(
            child: IconButton(
154
              onPressed: mockOnPressedFunction,
155
              icon: const Icon(Icons.ac_unit),
156 157 158 159 160 161 162 163 164 165 166 167
              tooltip: 'Test tooltip',
            ),
          ),
        ),
      ),
    );

    expect(find.byType(Tooltip), findsOneWidget);
    expect(find.byTooltip('Test tooltip'), findsOneWidget);

    await tester.tap(find.byTooltip('Test tooltip'));
    expect(mockOnPressedFunction.called, 1);
168 169 170 171
  });

  testWidgets('IconButton AppBar size', (WidgetTester tester) async {
    await tester.pumpWidget(
172 173 174
      MaterialApp(
        home: Scaffold(
          appBar: AppBar(
175
            actions: <Widget>[
176
              IconButton(
177 178 179 180 181 182
                padding: EdgeInsets.zero,
                onPressed: mockOnPressedFunction,
                icon: const Icon(Icons.ac_unit),
              ),
            ],
          ),
183 184
        ),
      ),
185 186
    );

187 188
    final RenderBox barBox = tester.renderObject(find.byType(AppBar));
    final RenderBox iconBox = tester.renderObject(find.byType(IconButton));
189 190
    expect(iconBox.size.height, equals(barBox.size.height));
  });
191 192 193 194

  // This test is very similar to the '...explicit splashColor and highlightColor' test
  // in buttons_test.dart. If you change this one, you may want to also change that one.
  testWidgets('IconButton with explicit splashColor and highlightColor', (WidgetTester tester) async {
195 196
    const Color directSplashColor = Color(0xFF00000F);
    const Color directHighlightColor = Color(0xFF0000F0);
197

Ian Hickson's avatar
Ian Hickson committed
198
    Widget buttonWidget = wrap(
199
        child: IconButton(
200 201 202 203 204 205 206 207
          icon: const Icon(Icons.android),
          splashColor: directSplashColor,
          highlightColor: directHighlightColor,
          onPressed: () { /* enable the button */ },
        ),
    );

    await tester.pumpWidget(
208 209
      Theme(
        data: ThemeData(),
210 211 212 213 214 215 216 217 218 219 220 221 222
        child: buttonWidget,
      ),
    );

    final Offset center = tester.getCenter(find.byType(IconButton));
    final TestGesture gesture = await tester.startGesture(center);
    await tester.pump(); // start gesture
    await tester.pump(const Duration(milliseconds: 200)); // wait for splash to be well under way

    expect(
      Material.of(tester.element(find.byType(IconButton))),
      paints
        ..circle(color: directSplashColor)
223
        ..circle(color: directHighlightColor),
224 225
    );

226 227
    const Color themeSplashColor1 = Color(0xFF000F00);
    const Color themeHighlightColor1 = Color(0xFF00FF00);
228

Ian Hickson's avatar
Ian Hickson committed
229
    buttonWidget = wrap(
230
        child: IconButton(
231 232 233 234 235 236
          icon: const Icon(Icons.android),
          onPressed: () { /* enable the button */ },
        ),
    );

    await tester.pumpWidget(
237 238
      Theme(
        data: ThemeData(
239 240 241 242 243 244 245 246 247 248 249
          highlightColor: themeHighlightColor1,
          splashColor: themeSplashColor1,
        ),
        child: buttonWidget,
      ),
    );

    expect(
      Material.of(tester.element(find.byType(IconButton))),
      paints
        ..circle(color: themeSplashColor1)
250
        ..circle(color: themeHighlightColor1),
251 252
    );

253 254
    const Color themeSplashColor2 = Color(0xFF002200);
    const Color themeHighlightColor2 = Color(0xFF001100);
255 256

    await tester.pumpWidget(
257 258
      Theme(
        data: ThemeData(
259 260 261 262 263 264 265 266 267 268 269
          highlightColor: themeHighlightColor2,
          splashColor: themeSplashColor2,
        ),
        child: buttonWidget, // same widget, so does not get updated because of us
      ),
    );

    expect(
      Material.of(tester.element(find.byType(IconButton))),
      paints
        ..circle(color: themeSplashColor2)
270
        ..circle(color: themeHighlightColor2),
271 272 273 274
    );

    await gesture.up();
  });
275

276
  testWidgets('IconButton Semantics (enabled)', (WidgetTester tester) async {
277
    final SemanticsTester semantics = SemanticsTester(tester);
278 279 280

    await tester.pumpWidget(
      wrap(
281
        child: IconButton(
282 283 284 285 286 287
          onPressed: mockOnPressedFunction,
          icon: const Icon(Icons.link, semanticLabel: 'link'),
        ),
      ),
    );

288
    expect(semantics, hasSemantics(TestSemantics.root(
289
      children: <TestSemantics>[
290
        TestSemantics.rootChild(
Dan Field's avatar
Dan Field committed
291
          rect: const Rect.fromLTRB(0.0, 0.0, 48.0, 48.0),
292
          actions: <SemanticsAction>[
293
            SemanticsAction.tap,
294 295 296 297
          ],
          flags: <SemanticsFlag>[
            SemanticsFlag.hasEnabledState,
            SemanticsFlag.isEnabled,
298
            SemanticsFlag.isButton,
299
          ],
300
          label: 'link',
301
        ),
302 303 304 305 306
      ]
    ), ignoreId: true, ignoreTransform: true));

    semantics.dispose();
  });
307 308

  testWidgets('IconButton Semantics (disabled)', (WidgetTester tester) async {
309
    final SemanticsTester semantics = SemanticsTester(tester);
310 311 312 313 314

    await tester.pumpWidget(
      wrap(
        child: const IconButton(
          onPressed: null,
315
          icon: Icon(Icons.link, semanticLabel: 'link'),
316 317 318 319
        ),
      ),
    );

320
    expect(semantics, hasSemantics(TestSemantics.root(
321
        children: <TestSemantics>[
322
          TestSemantics.rootChild(
Dan Field's avatar
Dan Field committed
323
            rect: const Rect.fromLTRB(0.0, 0.0, 48.0, 48.0),
324 325
            flags: <SemanticsFlag>[
              SemanticsFlag.hasEnabledState,
326
              SemanticsFlag.isButton,
327 328
            ],
            label: 'link',
329
          ),
330 331 332 333 334
        ]
    ), ignoreId: true, ignoreTransform: true));

    semantics.dispose();
  });
335
}
Ian Hickson's avatar
Ian Hickson committed
336 337

Widget wrap({ Widget child }) {
338
  return Directionality(
Ian Hickson's avatar
Ian Hickson committed
339
    textDirection: TextDirection.ltr,
340 341
    child: Material(
      child: Center(child: child),
Ian Hickson's avatar
Ian Hickson committed
342 343 344
    ),
  );
}