| // 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'; |
| |
| /// A [ListTile] containing a dropdown menu that exposes itself as an |
| /// "Adjustable" to screen readers (e.g. TalkBack on Android and VoiceOver on |
| /// iOS). |
| /// |
| /// This allows screen reader users to swipe up/down (on iOS) or use the volume |
| /// keys (on Android) to switch between the values in the dropdown menu. |
| /// Depending on what the values in the dropdown menu are this can be a more |
| /// intuitive way of switching values compared to exposing the content of the |
| /// drop down menu as a screen overlay from which the user can select. |
| /// |
| /// Users that do not use a screen reader will just see a regular dropdown menu. |
| class AdjustableDropdownListTile extends StatelessWidget { |
| const AdjustableDropdownListTile({ |
| this.label, |
| this.value, |
| this.items, |
| this.onChanged, |
| }); |
| |
| final String label; |
| final String value; |
| final List<String> items; |
| final ValueChanged<String> onChanged; |
| |
| @override |
| Widget build(BuildContext context) { |
| final int indexOfValue = items.indexOf(value); |
| assert(indexOfValue != -1); |
| |
| final bool canIncrease = indexOfValue < items.length - 1; |
| final bool canDecrease = indexOfValue > 0; |
| |
| return Semantics( |
| container: true, |
| label: label, |
| value: value, |
| increasedValue: canIncrease ? _increasedValue : null, |
| decreasedValue: canDecrease ? _decreasedValue : null, |
| onIncrease: canIncrease ? _performIncrease : null, |
| onDecrease: canDecrease ? _performDecrease : null, |
| child: ExcludeSemantics( |
| child: ListTile( |
| title: Text(label), |
| trailing: DropdownButton<String>( |
| value: value, |
| onChanged: onChanged, |
| items: items.map<DropdownMenuItem<String>>((String item) { |
| return DropdownMenuItem<String>( |
| value: item, |
| child: Text(item), |
| ); |
| }).toList(), |
| ), |
| ), |
| ), |
| ); |
| } |
| |
| String get _increasedValue { |
| final int indexOfValue = items.indexOf(value); |
| assert(indexOfValue < items.length - 1); |
| return items[indexOfValue + 1]; |
| } |
| |
| String get _decreasedValue { |
| final int indexOfValue = items.indexOf(value); |
| assert(indexOfValue > 0); |
| return items[indexOfValue - 1]; |
| } |
| |
| void _performIncrease() => onChanged(_increasedValue); |
| |
| void _performDecrease() => onChanged(_decreasedValue); |
| } |
| |
| class AdjustableDropdownExample extends StatefulWidget { |
| @override |
| AdjustableDropdownExampleState createState() => AdjustableDropdownExampleState(); |
| } |
| |
| class AdjustableDropdownExampleState extends State<AdjustableDropdownExample> { |
| |
| final List<String> items = <String>[ |
| '1 second', |
| '5 seconds', |
| '15 seconds', |
| '30 seconds', |
| '1 minute', |
| ]; |
| String timeout; |
| |
| @override |
| Widget build(BuildContext context) { |
| return MaterialApp( |
| home: Scaffold( |
| appBar: AppBar( |
| title: const Text('Adjustable DropDown'), |
| ), |
| body: ListView( |
| children: <Widget>[ |
| AdjustableDropdownListTile( |
| label: 'Timeout', |
| value: timeout ?? items[2], |
| items: items, |
| onChanged: (String value) { |
| setState(() { |
| timeout = value; |
| }); |
| }, |
| ), |
| ], |
| ), |
| ), |
| ); |
| } |
| } |
| |
| void main() { |
| runApp(AdjustableDropdownExample()); |
| } |
| |
| /* |
| Sample Catalog |
| |
| Title: AdjustableDropdownListTile |
| |
| Summary: A dropdown menu that exposes itself as an "Adjustable" to screen |
| readers. |
| |
| Description: |
| This app presents a dropdown menu to the user that exposes itself as an |
| "Adjustable" to screen readers (e.g. TalkBack on Android and VoiceOver on iOS). |
| This allows users of screen readers to cycle through the values of the dropdown |
| menu by swiping up or down on the screen with one finger (on iOS) or by using |
| the volume keys (on Android). Depending on the values in the dropdown this |
| behavior may be more intuitive to screen reader users compared to showing the |
| classical dropdown overlay on screen to choose a value. |
| |
| When the screen reader is turned off, the dropdown menu behaves like any |
| dropdown menu would. |
| |
| Classes: Semantics |
| |
| Sample: AdjustableDropdownListTile |
| |
| */ |