Skip to content

Commit

Permalink
Expose 'enable' property to allow the user to disable the SearchBar (…
Browse files Browse the repository at this point in the history
…#137388)

This exposes the `enabled` property of the `TextField` widget to the `Searchbar` widget.

## Related Issues

flutter/flutter#136943

Still missing tests
  • Loading branch information
Macacoazul01 committed Jan 11, 2024
1 parent 3590362 commit e281c39
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 54 deletions.
120 changes: 66 additions & 54 deletions packages/flutter/lib/src/material/search_anchor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const Curve _kViewFadeOnInterval = Interval(0.0, 1/2);
const Curve _kViewIconsFadeOnInterval = Interval(1/6, 2/6);
const Curve _kViewDividerFadeOnInterval = Interval(0.0, 1/6);
const Curve _kViewListFadeOnInterval = Interval(133 / _kOpenViewMilliseconds, 233 / _kOpenViewMilliseconds);
const double _kDisableSearchBarOpacity = 0.38;

/// Signature for a function that creates a [Widget] which is used to open a search view.
///
Expand Down Expand Up @@ -1115,6 +1116,7 @@ class SearchBar extends StatefulWidget {
this.textStyle,
this.hintStyle,
this.textCapitalization,
this.enabled = true,
this.autoFocus = false,
this.textInputAction,
this.keyboardType,
Expand Down Expand Up @@ -1239,6 +1241,9 @@ class SearchBar extends StatefulWidget {
/// {@macro flutter.widgets.editableText.textCapitalization}
final TextCapitalization? textCapitalization;

/// If false the text field is "disabled" so the SearchBar will ignore taps.
final bool enabled;

/// {@macro flutter.widgets.editableText.autofocus}
final bool autoFocus;

Expand Down Expand Up @@ -1340,62 +1345,69 @@ class _SearchBarState extends State<SearchBar> {

return ConstrainedBox(
constraints: widget.constraints ?? searchBarTheme.constraints ?? defaults.constraints!,
child: Material(
elevation: effectiveElevation!,
shadowColor: effectiveShadowColor,
color: effectiveBackgroundColor,
surfaceTintColor: effectiveSurfaceTintColor,
shape: effectiveShape?.copyWith(side: effectiveSide),
child: InkWell(
onTap: () {
widget.onTap?.call();
if (!_focusNode.hasFocus) {
_focusNode.requestFocus();
}
},
overlayColor: effectiveOverlayColor,
customBorder: effectiveShape?.copyWith(side: effectiveSide),
statesController: _internalStatesController,
child: Padding(
padding: effectivePadding!,
child: Row(
textDirection: textDirection,
children: <Widget>[
if (leading != null) leading,
Expanded(
child: Padding(
padding: effectivePadding,
child: TextField(
autofocus: widget.autoFocus,
onTap: widget.onTap,
onTapAlwaysCalled: true,
focusNode: _focusNode,
onChanged: widget.onChanged,
onSubmitted: widget.onSubmitted,
controller: widget.controller,
style: effectiveTextStyle,
decoration: InputDecoration(
hintText: widget.hintText,
).applyDefaults(InputDecorationTheme(
hintStyle: effectiveHintStyle,
// The configuration below is to make sure that the text field
// in `SearchBar` will not be overridden by the overall `InputDecorationTheme`
enabledBorder: InputBorder.none,
border: InputBorder.none,
focusedBorder: InputBorder.none,
contentPadding: EdgeInsets.zero,
// Setting `isDense` to true to allow the text field height to be
// smaller than 48.0
isDense: true,
)),
textCapitalization: effectiveTextCapitalization,
textInputAction: widget.textInputAction,
keyboardType: widget.keyboardType,
child: Opacity(
opacity: widget.enabled ? 1 : _kDisableSearchBarOpacity,
child: Material(
elevation: effectiveElevation!,
shadowColor: effectiveShadowColor,
color: effectiveBackgroundColor,
surfaceTintColor: effectiveSurfaceTintColor,
shape: effectiveShape?.copyWith(side: effectiveSide),
child: IgnorePointer(
ignoring: !widget.enabled,
child: InkWell(
onTap: () {
widget.onTap?.call();
if (!_focusNode.hasFocus) {
_focusNode.requestFocus();
}
},
overlayColor: effectiveOverlayColor,
customBorder: effectiveShape?.copyWith(side: effectiveSide),
statesController: _internalStatesController,
child: Padding(
padding: effectivePadding!,
child: Row(
textDirection: textDirection,
children: <Widget>[
if (leading != null) leading,
Expanded(
child: Padding(
padding: effectivePadding,
child: TextField(
autofocus: widget.autoFocus,
onTap: widget.onTap,
onTapAlwaysCalled: true,
focusNode: _focusNode,
onChanged: widget.onChanged,
onSubmitted: widget.onSubmitted,
controller: widget.controller,
style: effectiveTextStyle,
enabled: widget.enabled,
decoration: InputDecoration(
hintText: widget.hintText,
).applyDefaults(InputDecorationTheme(
hintStyle: effectiveHintStyle,
// The configuration below is to make sure that the text field
// in `SearchBar` will not be overridden by the overall `InputDecorationTheme`
enabledBorder: InputBorder.none,
border: InputBorder.none,
focusedBorder: InputBorder.none,
contentPadding: EdgeInsets.zero,
// Setting `isDense` to true to allow the text field height to be
// smaller than 48.0
isDense: true,
)),
textCapitalization: effectiveTextCapitalization,
textInputAction: widget.textInputAction,
keyboardType: widget.keyboardType,
),
),
),
),
if (trailing != null) ...trailing,
],
),
if (trailing != null) ...trailing,
],
),
),
),
),
Expand Down
68 changes: 68 additions & 0 deletions packages/flutter/test/material/search_anchor_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';

import '../widgets/semantics_tester.dart';

void main() {
// Returns the RenderEditable at the given index, or the first if not given.
RenderEditable findRenderEditable(WidgetTester tester, {int index = 0}) {
Expand Down Expand Up @@ -2945,6 +2947,72 @@ void main() {
final TextField textField = tester.widget(find.byType(TextField));
expect(textField.textInputAction, TextInputAction.previous);
});

testWidgets('Block entering text on disabled widget', (WidgetTester tester) async {
const String initValue = 'init';
final TextEditingController controller = TextEditingController(text: initValue);

await tester.pumpWidget(
MaterialApp(
home: Material(
child: Center(
child: SearchBar(
controller: controller,
enabled: false,
),
),
),
),
);

const String testValue = 'abcdefghi';
await tester.enterText(find.byType(SearchBar), testValue);
expect(controller.value.text, initValue);
});

testWidgets('Disabled SearchBar semantics node still contains value', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);

await tester.pumpWidget(
MaterialApp(
home: Material(
child: Center(
child: SearchBar(
controller: TextEditingController(text: 'text'),
enabled: false,
),
),
),
),
);

expect(semantics, includesNodeWith(actions: <SemanticsAction>[], value: 'text'));
semantics.dispose();
});

testWidgets('Check SearchBar opacity when disabled', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: Material(
child: Center(
child: SearchBar(
enabled: false,
),
),
),
),
);

final Finder searchBarFinder = find.byType(SearchBar);
expect(searchBarFinder, findsOneWidget);
final Finder opacityFinder = find.descendant(
of: searchBarFinder,
matching: find.byType(Opacity),
);
expect(opacityFinder, findsOneWidget);
final Opacity opacityWidget = tester.widget<Opacity>(opacityFinder);
expect(opacityWidget.opacity, 0.38);
});
}

Future<void> checkSearchBarDefaults(WidgetTester tester, ColorScheme colorScheme, Material material) async {
Expand Down

0 comments on commit e281c39

Please sign in to comment.