blob: ec893000c2e8330c922583291e2a0774c26f7970 [file] [log] [blame]
// 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 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'theme.dart';
// Examples can assume:
// late BuildContext context;
/// Defines the configuration of the search views created by the [SearchAnchor]
/// widget.
///
/// Descendant widgets obtain the current [SearchViewThemeData] object using
/// `SearchViewTheme.of(context)`.
///
/// Typically, a [SearchViewThemeData] is specified as part of the overall [Theme]
/// with [ThemeData.searchViewTheme]. Otherwise, [SearchViewTheme] can be used
/// to configure its own widget subtree.
///
/// All [SearchViewThemeData] properties are `null` by default. If any of these
/// properties are null, the search view will provide its own defaults.
///
/// See also:
///
/// * [ThemeData], which describes the overall theme for the application.
/// * [SearchBarThemeData], which describes the theme for the search bar itself in a
/// [SearchBar] widget.
/// * [SearchAnchor], which is used to open a search view route.
@immutable
class SearchViewThemeData with Diagnosticable {
/// Creates a theme that can be used for [ThemeData.searchViewTheme].
const SearchViewThemeData({
this.backgroundColor,
this.elevation,
this.surfaceTintColor,
this.constraints,
this.side,
this.shape,
this.headerTextStyle,
this.headerHintStyle,
this.dividerColor,
});
/// Overrides the default value of the [SearchAnchor.viewBackgroundColor].
final Color? backgroundColor;
/// Overrides the default value of the [SearchAnchor.viewElevation].
final double? elevation;
/// Overrides the default value of the [SearchAnchor.viewSurfaceTintColor].
final Color? surfaceTintColor;
/// Overrides the default value of the [SearchAnchor.viewSide].
final BorderSide? side;
/// Overrides the default value of the [SearchAnchor.viewShape].
final OutlinedBorder? shape;
/// Overrides the default value for [SearchAnchor.headerTextStyle].
final TextStyle? headerTextStyle;
/// Overrides the default value for [SearchAnchor.headerHintStyle].
final TextStyle? headerHintStyle;
/// Overrides the value of size constraints for [SearchAnchor.viewConstraints].
final BoxConstraints? constraints;
/// Overrides the value of the divider color for [SearchAnchor.dividerColor].
final Color? dividerColor;
/// Creates a copy of this object but with the given fields replaced with the
/// new values.
SearchViewThemeData copyWith({
Color? backgroundColor,
double? elevation,
Color? surfaceTintColor,
BorderSide? side,
OutlinedBorder? shape,
TextStyle? headerTextStyle,
TextStyle? headerHintStyle,
BoxConstraints? constraints,
Color? dividerColor,
}) {
return SearchViewThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor,
elevation: elevation ?? this.elevation,
surfaceTintColor: surfaceTintColor ?? this.surfaceTintColor,
side: side ?? this.side,
shape: shape ?? this.shape,
headerTextStyle: headerTextStyle ?? this.headerTextStyle,
headerHintStyle: headerHintStyle ?? this.headerHintStyle,
constraints: constraints ?? this.constraints,
dividerColor: dividerColor ?? this.dividerColor,
);
}
/// Linearly interpolate between two [SearchViewThemeData]s.
static SearchViewThemeData? lerp(SearchViewThemeData? a, SearchViewThemeData? b, double t) {
if (identical(a, b)) {
return a;
}
return SearchViewThemeData(
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
elevation: lerpDouble(a?.elevation, b?.elevation, t),
surfaceTintColor: Color.lerp(a?.surfaceTintColor, b?.surfaceTintColor, t),
side: _lerpSides(a?.side, b?.side, t),
shape: OutlinedBorder.lerp(a?.shape, b?.shape, t),
headerTextStyle: TextStyle.lerp(a?.headerTextStyle, b?.headerTextStyle, t),
headerHintStyle: TextStyle.lerp(a?.headerTextStyle, b?.headerTextStyle, t),
constraints: BoxConstraints.lerp(a?.constraints, b?.constraints, t),
dividerColor: Color.lerp(a?.dividerColor, b?.dividerColor, t),
);
}
@override
int get hashCode => Object.hash(
backgroundColor,
elevation,
surfaceTintColor,
side,
shape,
headerTextStyle,
headerHintStyle,
constraints,
dividerColor,
);
@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
if (other.runtimeType != runtimeType) {
return false;
}
return other is SearchViewThemeData
&& other.backgroundColor == backgroundColor
&& other.elevation == elevation
&& other.surfaceTintColor == surfaceTintColor
&& other.side == side
&& other.shape == shape
&& other.headerTextStyle == headerTextStyle
&& other.headerHintStyle == headerHintStyle
&& other.constraints == constraints
&& other.dividerColor == dividerColor;
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<Color?>('backgroundColor', backgroundColor, defaultValue: null));
properties.add(DiagnosticsProperty<double?>('elevation', elevation, defaultValue: null));
properties.add(DiagnosticsProperty<Color?>('surfaceTintColor', surfaceTintColor, defaultValue: null));
properties.add(DiagnosticsProperty<BorderSide?>('side', side, defaultValue: null));
properties.add(DiagnosticsProperty<OutlinedBorder?>('shape', shape, defaultValue: null));
properties.add(DiagnosticsProperty<TextStyle?>('headerTextStyle', headerTextStyle, defaultValue: null));
properties.add(DiagnosticsProperty<TextStyle?>('headerHintStyle', headerHintStyle, defaultValue: null));
properties.add(DiagnosticsProperty<BoxConstraints>('constraints', constraints, defaultValue: null));
properties.add(DiagnosticsProperty<Color?>('dividerColor', dividerColor, defaultValue: null));
}
// Special case because BorderSide.lerp() doesn't support null arguments
static BorderSide? _lerpSides(BorderSide? a, BorderSide? b, double t) {
if (a == null || b == null) {
return null;
}
if (identical(a, b)) {
return a;
}
return BorderSide.lerp(a, b, t);
}
}
/// An inherited widget that defines the configuration in this widget's
/// descendants for search view created by the [SearchAnchor] widget.
///
/// A search view theme can be specified as part of the overall Material theme using
/// [ThemeData.searchViewTheme].
///
/// See also:
///
/// * [SearchViewThemeData], which describes the actual configuration of a search view
/// theme.
class SearchViewTheme extends InheritedWidget {
/// Creates a const theme that controls the configurations for the search view
/// created by the [SearchAnchor] widget.
const SearchViewTheme({
super.key,
required this.data,
required super.child,
});
/// The properties used for all descendant [SearchAnchor] widgets.
final SearchViewThemeData data;
/// Returns the configuration [data] from the closest [SearchViewTheme] ancestor.
/// If there is no ancestor, it returns [ThemeData.searchViewTheme].
///
/// Typical usage is as follows:
///
/// ```dart
/// SearchViewThemeData theme = SearchViewTheme.of(context);
/// ```
static SearchViewThemeData of(BuildContext context) {
final SearchViewTheme? searchViewTheme = context.dependOnInheritedWidgetOfExactType<SearchViewTheme>();
return searchViewTheme?.data ?? Theme.of(context).searchViewTheme;
}
@override
bool updateShouldNotify(SearchViewTheme oldWidget) => data != oldWidget.data;
}