icon_button_test.dart 9.42 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 94 95 96
  });

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

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

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

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

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

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

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

    await tester.pumpWidget(
152 153 154 155
      MaterialApp(
        home: Material(
          child: Center(
            child: IconButton(
156
              onPressed: mockOnPressedFunction,
157
              icon: const Icon(Icons.ac_unit),
158 159 160 161 162 163 164 165 166 167 168 169
              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);
170 171 172 173
  });

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

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

  // 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 {
197 198
    const Color directSplashColor = Color(0xFF00000F);
    const Color directHighlightColor = Color(0xFF0000F0);
199

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

    await tester.pumpWidget(
210 211
      Theme(
        data: ThemeData(),
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
        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)
        ..circle(color: directHighlightColor)
    );

228 229
    const Color themeSplashColor1 = Color(0xFF000F00);
    const Color themeHighlightColor1 = Color(0xFF00FF00);
230

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

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

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

255 256
    const Color themeSplashColor2 = Color(0xFF002200);
    const Color themeHighlightColor2 = Color(0xFF001100);
257 258

    await tester.pumpWidget(
259 260
      Theme(
        data: ThemeData(
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
          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)
        ..circle(color: themeHighlightColor2)
    );

    await gesture.up();
  });
277

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

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

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

    semantics.dispose();
  });
309 310

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

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

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

    semantics.dispose();
  });
337
}
Ian Hickson's avatar
Ian Hickson committed
338 339

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