Commit ffa5710d by Rizal Hermawan

progress on updating the flutter version to 2.5.x

parents
{
"configVersion": 2,
"packages": [
{
"name": "async",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/async-2.8.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "boolean_selector",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "characters",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "charcode",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "clock",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "collection",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "fake_async",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/packages/flutter",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter_test",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/packages/flutter_test",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "matcher",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "meta",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/meta-1.7.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "path",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/path-1.8.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "sky_engine",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/bin/cache/pkg/sky_engine",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "source_span",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "stack_trace",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "stream_channel",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "string_scanner",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "term_glyph",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "test_api",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.2",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "typed_data",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "vector_math",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "material_floating_search_bar_2",
"rootUri": "../",
"packageUri": "lib/",
"languageVersion": "2.12"
}
],
"generated": "2023-12-04T04:51:52.780646Z",
"generator": "pub",
"generatorVersion": "2.14.4"
}
async
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/async-2.8.1/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/async-2.8.1/lib/
boolean_selector
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib/
characters
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0/lib/
charcode
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/lib/
clock
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib/
collection
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0/lib/
fake_async
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0/lib/
matcher
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10/lib/
meta
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/meta-1.7.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/meta-1.7.0/lib/
path
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/path-1.8.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/path-1.8.0/lib/
source_span
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1/lib/
stack_trace
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/
stream_channel
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib/
string_scanner
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib/
term_glyph
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib/
test_api
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.2/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.2/lib/
typed_data
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0/lib/
vector_math
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0/lib/
sky_engine
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/bin/cache/pkg/sky_engine/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/bin/cache/pkg/sky_engine/lib/
flutter
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/packages/flutter/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/packages/flutter/lib/
flutter_test
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/packages/flutter_test/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/packages/flutter_test/lib/
material_floating_search_bar_2
2.12
file:///C:/24%20Rizal%20Hermawan/job/mobile/codingan/edit_pub/material_floating_search_bar_2-0.5.0/
file:///C:/24%20Rizal%20Hermawan/job/mobile/codingan/edit_pub/material_floating_search_bar_2-0.5.0/lib/
2
2.5.3
\ No newline at end of file
# This file is deprecated. Tools should instead consume
# `.dart_tool/package_config.json`.
#
# For more info see: https://dart.dev/go/dot-packages-deprecation
#
# Generated by pub on 2023-12-04 11:51:52.747949.
async:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/async-2.8.1/lib/
boolean_selector:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib/
characters:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0/lib/
charcode:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/lib/
clock:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib/
collection:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0/lib/
fake_async:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0/lib/
flutter:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/packages/flutter/lib/
flutter_test:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/packages/flutter_test/lib/
matcher:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10/lib/
meta:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/meta-1.7.0/lib/
path:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/path-1.8.0/lib/
sky_engine:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/bin/cache/pkg/sky_engine/lib/
source_span:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1/lib/
stack_trace:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/
stream_channel:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib/
string_scanner:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib/
term_glyph:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib/
test_api:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.2/lib/
typed_data:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0/lib/
vector_math:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0/lib/
material_floating_search_bar_2:lib/
## 0.5.0
* **Breaking** Made package ready for a change in `FixedScrollMetrics` Flutter 3.10.0. Minimum Dart version required is now 3.0.0
## 0.4.2
* **Breaking** Replaced deprecated `ToolbarOptions` with `EditableTextContextMenuBuilder`
## 0.4.1
* **Retracted** Due to incorrect sdk constraints
## 0.4.0
* **Retracted** Due to incorrect sdk constraints
## 0.3.8
* **Initial Release** of continued package
## 0.3.7
* **Fixed** #72
* **Improved** Hide elevation when search bar is hidden
## 0.3.6
* **Fixed** #62
## 0.3.5
* **Fixed** #33
## 0.3.3
* **Fixed** #30
* **Breaking** Renamed `maxWidth` to `width` and `openMaxWidth` to `openWidth`
## 0.3.1
* **Fixed** Minor bugs
* **Fixed** Example application
## 0.3.0
* **Added** NNBD support
## 0.2.6
* **Fixed** `FloatingSearchBarNotifier` notifications on bounce backs/overscroll
## 0.2.5
* **Fixed** High CPU idle usage (#20)
* **Improved** Small floating search bar height (#19)
## 0.2.4
* **Fixed** Bugs
## 0.2.3
* **Breaking** Renamed `showDrawerHamburger` to `automaticallyImplyDrawerHamburger`
* **Added** `automaticallyImplyBackButton` flag (#12, #14)
* **Improved** RTL support (#13)
## 0.2.2
* **Fixed** Bugs
## 0.2.0
* **Added** `FloatingSearchAppBar`
## 0.1.7
* **Fixed** Scrolling
## 0.1.5
* **Fixed** Bugs
## 0.1.0
* **Initial release**
\ No newline at end of file
MIT License
Copyright (c) 2020 bxqm
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
\ No newline at end of file
# Material Floating Search Bar
[![pub package](https://img.shields.io/pub/v/material_floating_search_bar_2.svg)](https://pub.dartlang.org/packages/material_floating_search_bar_2)
[![GitHub Stars](https://img.shields.io/github/stars/AlwinFassbender/material_floating_search_bar.svg?logo=github)](https://github.com/AlwinFassbender/material_floating_search_bar)
Continued version of the [material_floating_search_bar package](https://pub.dev/packages/material_floating_search_bar), as the [original repository](https://github.com/bxqm/material_floating_search_bar) was deleted. This package serves as a continuation of bxqm's popular work, similar to the [implicitly_animated_reorderable_list_2 package](https://pub.dev/packages/implicitly_animated_reorderable_list_2).
A Flutter implementation of an expandable floating search bar, also known as [persistent search](https://m2.material.io/design/navigation/search.html#persistent-search), similar to the ones used extensively by Google in their own apps.
If you like this package, consider supporting it by giving it a star on [GitHub](https://github.com/AlwinFassbender/material_floating_search_bar) and a like on [pub.dev](https://pub.dev/packages/material_floating_search_bar) :heart:
<p>
<img width="216px" alt="CircularFloatingSearchBarTransition" src="https://raw.githubusercontent.com/AlwinFassbender/material_floating_search_bar/master/assets/circular_example.gif"/>
<img width="216px" alt="ExpandingFloatingSearchBarTransition" src="https://raw.githubusercontent.com/AlwinFassbender/material_floating_search_bar/master/assets/expanding_example.gif"/>
<img width="216px" alt="SlideFadeFloatingSearchBarTransition" src="https://raw.githubusercontent.com/AlwinFassbender/material_floating_search_bar/master/assets/slide_fade_example.gif"/>
</p>
Click [here](https://github.com/AlwinFassbender/material_floating_search_bar/blob/master/example/lib/main.dart) to view the full example.
## Installing
See [Installing](https://pub.dev/packages/material_floating_search_bar_2/install)
## Usage
A `FloatingSearchBar` should be placed above your main content in your widget tree and be allowed to fill all the available space.
```dart
@override
Widget build(BuildContext context) {
return Scaffold(
// This is handled by the search bar itself.
resizeToAvoidBottomInset: false,
body: Stack(
fit: StackFit.expand,
children: [
buildMap(),
buildBottomNavigationBar(),
buildFloatingSearchBar(),
],
),
);
}
Widget buildFloatingSearchBar() {
final isPortrait = MediaQuery.of(context).orientation == Orientation.portrait;
return FloatingSearchBar(
hint: 'Search...',
scrollPadding: const EdgeInsets.only(top: 16, bottom: 56),
transitionDuration: const Duration(milliseconds: 800),
transitionCurve: Curves.easeInOut,
physics: const BouncingScrollPhysics(),
axisAlignment: isPortrait ? 0.0 : -1.0,
openAxisAlignment: 0.0,
width: isPortrait ? 600 : 500,
debounceDelay: const Duration(milliseconds: 500),
onQueryChanged: (query) {
// Call your model, bloc, controller here.
},
// Specify a custom transition to be used for
// animating between opened and closed stated.
transition: CircularFloatingSearchBarTransition(),
actions: [
FloatingSearchBarAction(
showIfOpened: false,
child: CircularButton(
icon: const Icon(Icons.place),
onPressed: () {},
),
),
FloatingSearchBarAction.searchToClear(
showIfClosed: false,
),
],
builder: (context, transition) {
return ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Material(
color: Colors.white,
elevation: 4.0,
child: Column(
mainAxisSize: MainAxisSize.min,
children: Colors.accents.map((color) {
return Container(height: 112, color: color);
}).toList(),
),
),
);
},
);
}
```
#### Usage with `Scrollables`
By default, the `Widget` returned by the `builder` is not allowed to have an unbounded (infinite) height. This is necessary in order for the search bar to be able to dismiss itself, when the user taps below the area of the child. (For example, when you have a list of items but there are not enough items to fill the whole screen, as shown in the gifs above, the user would expect to be able to close the search bar when tapping below the last item in the list).
Therefore, `shrinkWrap` should be set to `true` on all `Scrollables` and `physics` to `NeverScrollableScrollPhysics`. On `Columns`, the `mainAxisSize` should be set to `MainAxisSize.min`.
If you don't want this behavior, you can set the `isScrollControlled` flag to `true`. Then you are allowed to use expanding `Widgets` such as `Scrollables` with the caveat that the search bar may not be able to detect taps on the backdrop area.
### Customizations
There are many customization options:
| Field | Description |
| --------------------------- | ----------------------- |
| `body` | The widget displayed below the `FloatingSearchBar`. <br><br> This is useful, if the `FloatingSearchBar` should react to scroll events (i.e. hide from view when a `Scrollable` is being scrolled down and show it again when scrolled up). See [here](#Scrolling) for more info.
| `accentColor` | The color used for elements such as the progress indicator. <br><br> Defaults to the themes accent color if not specified.
| `backgroundColor` | The color of the card. <br><br> If not specified, defaults to `theme.cardColor`.
| `shadowColor` | The color of the shadow drawn when `elevation > 0`. <br><br> If not specified, defaults to `Colors.black54`.
| `iconColor` | When specified, overrides the themes icon color for this `FloatingSearchBar`, for example to easily adjust the icon color for all `actions` and `leadingActions`.
| `backdropColor` | The color that fills the available space when the `FloatingSearchBar` is opened. <br><br> Typically a black-ish color. <br><br> If not specified, defaults to `Colors.black26`.
| `margins` | The insets from the edges of its parent. <br><br> This can be used to position the `FloatingSearchBar`. <br><br> If not specifed, the `FloatingSearchBar` will try to position itself at the top offsetted by `MediaQuery.of(context).viewPadding.top` to avoid the status bar.
| `padding` | The padding of the card. <br><br> Only the horizontal values will be honored.
| `insets` | The padding between `leadingActions`, the input field and `actions` respectively. <br><br> Only the horizontal values will be honored.
| `height` | The height of the card. <br><br> If not specified, defaults to `48.0` pixels.
| `elevation` | The elevation of the card.
| `width` | The width of the `FloatingSearchBar`. <br><br> By default the `FloatingSearchBar` will expand to fill all the available width. <br><br> This value can be set to avoid this.
| `openWidth` | The width of the `FloatingSearchBar` when opened. <br><br> This can be used, when the max width when opened should be different from the one specified by `maxWidth`. <br><br> When not specified, will use the value of `maxWidth`.
| `axisAlignment` | How the `FloatingSearchBar` should be aligned when the available width is bigger than the width specified by `maxWidth`. <br><br> When not specified, defaults to `0.0` which centers the `FloatingSearchBar`.
| `openAxisAlignment` | How the `FloatingSearchBar` should be aligned when the available width is bigger than the width specified by `openMaxWidth`. <br><br> When not specified, will use the value of `axisAlignment`.
| `border` | The border of the card.
| `borderRadius` | The `BorderRadius` of the card. <br><br> When not specified, defaults to `BorderRadius.circular(4)`.
| `hintStyle` | The `TextStyle` for the hint in the `TextField`.
| `queryStyle` | The `TextStyle` for the input in the `TextField`.
| `clearQueryOnClose` | Whether the current query should be cleared when the `FloatingSearchBar` was closed. <br><br> When not specifed, defaults to `true`.
| `automaticallyImplyDrawerHamburger`| Whether a hamburger menu should be shown when there is a `Scaffold` with a `Drawer` in the widget tree.
| `closeOnBackdropTap` | Whether the `FloatingSearchBar` should be closed when the backdrop was tapped. <br><br> When not specified, defaults to `true`.
| `automaticallyImplyBackButton`| Whether to automatically display a back button if the enclosing route can be popped.<br><br> When not specified, defaults to `true`.
| `progress` | The progress of the `LinearProgressIndicator` inside the card. <br><br> When set to a `double` between `0..1`, will show show a determined `LinearProgressIndicator`. <br><br> When set to `true`, the `FloatingSearchBar` will show an indetermined `LinearProgressIndicator`. <br><br> When `null` or `false`, will hide the `LinearProgressIndicator`.
| `transitionDuration` | The duration of the animation between opened and closed state.
| `transitionCurve` | The curve for the animation between opened and closed state.
| `debounceDelay` | The delay between the time the user stopped typing and the invocation of the `onQueryChanged` callback. <br><br> This is useful for example if you want to avoid doing expensive tasks, such as making a network call, for every single character.
| `title` | A widget that is shown in place of the `TextField` when the `FloatingSearchBar` is closed.
| `hint` | The text value of the hint of the `TextField`.
| `actions` | A list of widgets displayed in a row after the `TextField`. <br><br> Consider using `FloatingSearchBarActions` for more advanced actions that can interact with the `FloatingSearchBar`. <br><br> In LTR languages, they will be displayed to the left of the `TextField`.
| `leadingActions` | A list of widgets displayed in a row before the `TextField`. <br><br> Consider using `FloatingSearchBarActions` for more advanced actions that can interact with the `FloatingSearchBar`. <br><br> In LTR languages, they will be displayed to the right of the `TextField`.
| `onQueryChanged` | A callback that gets invoked when the input of the query inside the `TextField` changed.
| `onSubmitted` | A callback that gets invoked when the user submitted their query (e.g. hit the search button).
| `onFocusChanged` | A callback that gets invoked when the `FloatingSearchBar` receives or looses focus.
| `transition` | The transition to be used for animating between closed and opened state. See below for a list of all available transitions.
| `builder` | The builder for the body of this `FloatingSearchBar`. <br><br> Usually, a list of items. Note that unless `isScrollControlled` is set to `true`, the body of a `FloatingSearchBar` must not have an unbounded height meaning that `shrinkWrap` should be set to `true` on all `Scrollables`.
| `controller` | The controller for this `FloatingSearchBar` which can be used to programatically open, close, show or hide the `FloatingSearchBar`.
| `isScrollControlled` | Whether the body of this `FloatingSearchBar` is using its own `Scrollable`. <br><br> This will allow the body of the `FloatingSearchBar` to have an unbounded height. <br><br> Note that when set to `true`, the `FloatingSearchBar` won't be able to dismiss itself when tapped below the height of child inside the `Scrollable`, when the child is smaller than the avaialble height.
| `initiallyHidden` | Set to `true` to hide the search bar intially. You have to call `show` on the controller to in order to show the `FloatingSearchBar` again,
### Transitions
As of now there are three types of transitions that are exemplified above:
| Transition | Description |
| -------------------------------------- | ------------------------------------- |
| `CircularFloatingSearchBarTransition` | Clips its child in an expanding circle.
| `ExpandingFloatingSearchBarTransition` | Fills all the available space with the background of the `FloatingSearchBar`. Similar to the ones used in many Google apps like Gmail.
| `SlideFadeFloatingSearchBarTransition` | Vertically slides and fades its child.
You can also easily create you own custom transition by extending `FloatingSearchBarTransition`.
### Scrolling
<img width="216px" alt="Scrolling" src="https://raw.githubusercontent.com/bxqm/material_floating_search_bar/master/assets/scroll_demo.gif"/>
A common behavior for floating search bars is to disappear when the user scrolls a `Scrollable` down and appear again when scrolling up. This can be easily achieved by passing your `Widget` to the `body` field of `FloatingSearchBar`. This way `FloatingSearchBar` can listen for `ScrollNotifications`. In order that the `FloatingSearchBar` doesn't interact with every `Scrollable` below in the widget tree, you should wrap every `Scrollable` that should interact with the `FloatingSearchBar` inside a `FloatingSearchBarScrollNotifier`.
#### Example
```dart
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FloatingSearchBar(
// Your pages or just a simple Scaffold...
body: IndexedStack(
children: [
MyAwesomePage(),
],
),
);
}
}
class MyAwesomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
/// Wrap your Scrollable in a FloatingSearchBarScrollNotifier
/// to indicate that the FloatingSearchBar should react to
/// scroll events from this Scrollable.
return FloatingSearchBarScrollNotifier(
child: ListView.builder(
itemCount: 42,
itemBuilder: (_, index) => Item('Item $index'),
),
);
}
}
```
### FloatingSearchBarController
The `FloatingSearchBarController` can be used to control the
`FloatingSearchBar` _(duh)_.
| Method | Description |
| -------------------------------------- | ------------------------------------- |
| `open()` | Expands the `FloatingSearchBar`
| `close()` | Closes the `FloatingSearchBar`
| `show()` | Reveals the `FloatingSearchBar` when it was previously hidden using `hide()`.
| `hide()` | Visually hides the `FloatingSearchBar` (slides off screen)
| `query` | Sets the input of the query inside the `InputField`
| `clear()` | Clears the query
## Floating Search App Bar
Sometimes a `FloatingSearchBar` might not be the most appriopriate search method for your use case. For this reason there is also the `FloatingSearchAppBar`. It is a normal `AppBar` with easy search integration very similar to the normal `FloatingSearchBar`.
<img width="312px" alt="FloatingSearchAppBar example" src="https://raw.githubusercontent.com/bxqm/material_floating_search_bar/master/assets/floating_search_app_bar_demo.png"/>
### Additional Customizations
In addition to most of the fields from the `FloatingSearchBar`, `FloatingSearchAppBar` has the following additional fields:
| Field | Description |
| --------------------------- | ----------------------- |
| `colorOnScroll` | The color of the bar when a `Scrollable` inside the `body` was scrolled (i.e. the `Scrollable` is not at the top)
| `liftOnScrollElevation` | The elevation of the bar when a `Scrollable` inside the `body` was scrolled (i.e. the `Scrollable` is not at the top)
| `alwaysOpened` | Whether the bar should be always in opened state.<br><br>This is useful for example, if you have a page dedicated only for search.
| `hideKeyboardOnDownScroll` | Hides the keyboard if a `Scrollable` inside the `body` was scrolled and shows it again when the user scrolls to the top.
\ No newline at end of file
# Specify analysis options.
#
# For a list of lints, see: http://dart-lang.github.io/linter/lints/
# See the configuration guide for more
# https://github.com/dart-lang/sdk/tree/main/pkg/analyzer#configuring-the-analyzer
#
# There are other similar analysis options files in the flutter repos,
# which should be kept in sync with this file:
#
# - analysis_options.yaml (this file)
# - https://github.com/flutter/plugins/blob/master/analysis_options.yaml
# - https://github.com/flutter/engine/blob/master/analysis_options.yaml
# - https://github.com/flutter/packages/blob/master/analysis_options.yaml
#
# This file contains the analysis options used for code in the flutter/flutter
# repository.
analyzer:
language:
strict-casts: true
strict-raw-types: true
errors:
# allow self-reference to deprecated members (we do this because otherwise we have
# to annotate every member in every test, assert, etc, when we deprecate something)
deprecated_member_use_from_same_package: ignore
exclude:
- "bin/cache/**"
# Ignore protoc generated files
- "dev/conductor/lib/proto/*"
linter:
rules:
# This list is derived from the list of all available lints located at
# https://github.com/dart-lang/linter/blob/master/example/all.yaml
- always_declare_return_types
- always_put_control_body_on_new_line
# - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219
- always_specify_types
# - always_use_package_imports # we do this commonly
- annotate_overrides
# - avoid_annotating_with_dynamic # conflicts with always_specify_types
- avoid_bool_literals_in_conditional_expressions
# - avoid_catches_without_on_clauses # blocked on https://github.com/dart-lang/linter/issues/3023
# - avoid_catching_errors # blocked on https://github.com/dart-lang/linter/issues/3023
# avoid_classes_with_only_static_members # # we do this commonly for `abstract final class`es
- avoid_double_and_int_checks
- avoid_dynamic_calls
- avoid_empty_else
- avoid_equals_and_hash_code_on_mutable_classes
- avoid_escaping_inner_quotes
- avoid_field_initializers_in_const_classes
# - avoid_final_parameters # incompatible with prefer_final_parameters
- avoid_function_literals_in_foreach_calls
- avoid_implementing_value_types
- avoid_init_to_null
- avoid_js_rounded_ints
# - avoid_multiple_declarations_per_line # seems to be a stylistic choice we don't subscribe to
- avoid_null_checks_in_equality_operators
# - avoid_positional_boolean_parameters # would have been nice to enable this but by now there's too many places that break it
- avoid_print
# - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356)
- avoid_redundant_argument_values
- avoid_relative_lib_imports
- avoid_renaming_method_parameters
- avoid_return_types_on_setters
- avoid_returning_null_for_void
# - avoid_returning_this # there are enough valid reasons to return `this` that this lint ends up with too many false positives
- avoid_setters_without_getters
- avoid_shadowing_type_parameters
- avoid_single_cascade_in_expression_statements
- avoid_slow_async_io
- avoid_type_to_string
- avoid_types_as_parameter_names
# - avoid_types_on_closure_parameters # conflicts with always_specify_types
- avoid_unnecessary_containers
- avoid_unused_constructor_parameters
- avoid_void_async
# - avoid_web_libraries_in_flutter # we use web libraries in web-specific code, and our tests prevent us from using them elsewhere
- await_only_futures
- camel_case_extensions
- camel_case_types
- cancel_subscriptions
# - cascade_invocations # doesn't match the typical style of this repo
- cast_nullable_to_non_nullable
# - close_sinks # not reliable enough
- collection_methods_unrelated_type
- combinators_ordering
# - comment_references # blocked on https://github.com/dart-lang/linter/issues/1142
- conditional_uri_does_not_exist
# - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204
- control_flow_in_finally
- curly_braces_in_flow_control_structures
- dangling_library_doc_comments
- depend_on_referenced_packages
- deprecated_consistency
# - diagnostic_describe_all_properties # enabled only at the framework level (packages/flutter/lib)
- directives_ordering
# - discarded_futures # too many false positives, similar to unawaited_futures
# - do_not_use_environment # there are appropriate times to use the environment, especially in our tests and build logic
- empty_catches
- empty_constructor_bodies
- empty_statements
- eol_at_end_of_file
- exhaustive_cases
- file_names
- flutter_style_todos
- hash_and_equals
- implementation_imports
- implicit_call_tearoffs
- invalid_case_patterns
- iterable_contains_unrelated_type
# - join_return_with_assignment # not required by flutter style
- leading_newlines_in_multiline_strings
- library_annotations
- library_names
- library_prefixes
- library_private_types_in_public_api
# - lines_longer_than_80_chars # not required by flutter style
- list_remove_unrelated_type
# - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/linter/issues/453
- missing_whitespace_between_adjacent_strings
- no_adjacent_strings_in_list
- no_default_cases
- no_duplicate_case_values
- no_leading_underscores_for_library_prefixes
- no_leading_underscores_for_local_identifiers
- no_logic_in_create_state
# - no_runtimeType_toString # ok in tests; we enable this only in packages/
- non_constant_identifier_names
- noop_primitive_operations
- null_check_on_nullable_type_parameter
- null_closures
# - omit_local_variable_types # opposite of always_specify_types
# - one_member_abstracts # too many false positives
- only_throw_errors # this does get disabled in a few places where we have legacy code that uses strings et al
- overridden_fields
- package_api_docs
- package_names
- package_prefixed_library_names
# - parameter_assignments # we do this commonly
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
# - prefer_asserts_with_message # not required by flutter style
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
# - prefer_constructors_over_static_methods # far too many false positives
- prefer_contains
# - prefer_double_quotes # opposite of prefer_single_quotes
# - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
- prefer_final_fields
- prefer_final_in_for_each
- prefer_final_locals
# - prefer_final_parameters # we should enable this one day when it can be auto-fixed (https://github.com/dart-lang/linter/issues/3104), see also parameter_assignments
- prefer_for_elements_to_map_fromIterable
- prefer_foreach
- prefer_function_declarations_over_variables
- prefer_generic_function_type_aliases
- prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
# - prefer_int_literals # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#use-double-literals-for-double-constants
- prefer_interpolation_to_compose_strings
- prefer_is_empty
- prefer_is_not_empty
- prefer_is_not_operator
- prefer_iterable_whereType
- prefer_mixin
# - prefer_null_aware_method_calls # "call()" is confusing to people new to the language since it's not documented anywhere
- prefer_null_aware_operators
- prefer_relative_imports
- prefer_single_quotes
- prefer_spread_collections
- prefer_typing_uninitialized_variables
- prefer_void_to_null
- provide_deprecation_message
# - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml
- recursive_getters
# - require_trailing_commas # would be nice, but requires a lot of manual work: 10,000+ code locations would need to be reformatted by hand after bulk fix is applied
- secure_pubspec_urls
- sized_box_for_whitespace
- sized_box_shrink_expand
- slash_for_doc_comments
- sort_child_properties_last
- sort_constructors_first
# - sort_pub_dependencies # prevents separating pinned transitive dependencies
- sort_unnamed_constructors_first
- test_types_in_equals
- throw_in_finally
- tighten_type_of_initializing_formals
# - type_annotate_public_apis # subset of always_specify_types
- type_init_formals
# - unawaited_futures # too many false positives, especially with the way AnimationController works
- unnecessary_await_in_return
- unnecessary_brace_in_string_interps
- unnecessary_breaks
- unnecessary_const
- unnecessary_constructor_name
# - unnecessary_final # conflicts with prefer_final_locals
- unnecessary_getters_setters
# - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498
- unnecessary_late
- unnecessary_library_directive
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_aware_operator_on_extension_on_nullable
- unnecessary_null_checks
- unnecessary_null_in_if_null_operators
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_overrides
- unnecessary_parenthesis
# - unnecessary_raw_strings # what's "necessary" is a matter of opinion; consistency across strings can help readability more than this lint
- unnecessary_statements
- unnecessary_string_escapes
- unnecessary_string_interpolations
- unnecessary_this
- unnecessary_to_list_in_spreads
# - unreachable_from_main # Do not enable this rule until it is un-marked as "experimental" and carefully re-evaluated.
- unrelated_type_equality_checks
- unsafe_html
- use_build_context_synchronously
- use_colored_box
# - use_decorated_box # leads to bugs: DecoratedBox and Container are not equivalent (Container inserts extra padding)
- use_enums
- use_full_hex_values_for_flutter_colors
- use_function_type_syntax_for_parameters
- use_if_null_to_convert_nulls_to_bools
- use_is_even_rather_than_modulo
- use_key_in_widget_constructors
- use_late_for_private_fields_and_variables
- use_named_constants
- use_raw_strings
- use_rethrow_when_possible
- use_setters_to_change_properties
# - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182
- use_string_in_part_of_directives
- use_super_parameters
- use_test_throws_matchers
# - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
- valid_regexps
- void_checks
\ No newline at end of file
{
"configVersion": 2,
"packages": [
{
"name": "async",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/async-2.8.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "boolean_selector",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "characters",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "charcode",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "clock",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "collection",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "fake_async",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/packages/flutter",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter_test",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/packages/flutter_test",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "http",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/http-0.13.5",
"packageUri": "lib/",
"languageVersion": "2.14"
},
{
"name": "http_parser",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/http_parser-4.0.2",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "implicitly_animated_reorderable_list_2",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/implicitly_animated_reorderable_list_2-0.5.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "matcher",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "material_design_icons_flutter",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/material_design_icons_flutter-6.0.7296",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "material_floating_search_bar_2",
"rootUri": "../../",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "meta",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/meta-1.7.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "nested",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/nested-1.0.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "path",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/path-1.8.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "provider",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/provider-6.1.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "sky_engine",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/bin/cache/pkg/sky_engine",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "source_span",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "stack_trace",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "stream_channel",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "string_scanner",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "term_glyph",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "test_api",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.2",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "typed_data",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "vector_math",
"rootUri": "file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "example",
"rootUri": "../",
"packageUri": "lib/",
"languageVersion": "2.12"
}
],
"generated": "2023-12-04T04:51:53.833263Z",
"generator": "pub",
"generatorVersion": "2.14.4"
}
async
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/async-2.8.1/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/async-2.8.1/lib/
boolean_selector
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib/
characters
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0/lib/
charcode
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/lib/
clock
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib/
collection
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0/lib/
fake_async
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0/lib/
http
2.14
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/http-0.13.5/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/http-0.13.5/lib/
http_parser
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/http_parser-4.0.2/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/http_parser-4.0.2/lib/
implicitly_animated_reorderable_list_2
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/implicitly_animated_reorderable_list_2-0.5.1/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/implicitly_animated_reorderable_list_2-0.5.1/lib/
matcher
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10/lib/
material_design_icons_flutter
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/material_design_icons_flutter-6.0.7296/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/material_design_icons_flutter-6.0.7296/lib/
meta
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/meta-1.7.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/meta-1.7.0/lib/
nested
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/nested-1.0.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/nested-1.0.0/lib/
path
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/path-1.8.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/path-1.8.0/lib/
provider
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/provider-6.1.1/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/provider-6.1.1/lib/
source_span
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1/lib/
stack_trace
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/
stream_channel
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib/
string_scanner
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib/
term_glyph
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib/
test_api
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.2/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.2/lib/
typed_data
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0/lib/
vector_math
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0/lib/
sky_engine
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/bin/cache/pkg/sky_engine/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/bin/cache/pkg/sky_engine/lib/
flutter
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/packages/flutter/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/packages/flutter/lib/
flutter_test
2.12
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/packages/flutter_test/
file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/packages/flutter_test/lib/
material_floating_search_bar_2
2.12
file:///C:/24%20Rizal%20Hermawan/job/mobile/codingan/edit_pub/material_floating_search_bar_2-0.5.0/
file:///C:/24%20Rizal%20Hermawan/job/mobile/codingan/edit_pub/material_floating_search_bar_2-0.5.0/lib/
example
2.12
file:///C:/24%20Rizal%20Hermawan/job/mobile/codingan/edit_pub/material_floating_search_bar_2-0.5.0/example/
file:///C:/24%20Rizal%20Hermawan/job/mobile/codingan/edit_pub/material_floating_search_bar_2-0.5.0/example/lib/
2
2.5.3
\ No newline at end of file
# This file is deprecated. Tools should instead consume
# `.dart_tool/package_config.json`.
#
# For more info see: https://dart.dev/go/dot-packages-deprecation
#
# Generated by pub on 2023-12-04 11:51:53.816327.
async:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/async-2.8.1/lib/
boolean_selector:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/boolean_selector-2.1.0/lib/
characters:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/characters-1.1.0/lib/
charcode:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/charcode-1.3.1/lib/
clock:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/clock-1.1.0/lib/
collection:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/collection-1.15.0/lib/
fake_async:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0/lib/
flutter:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/packages/flutter/lib/
flutter_test:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/packages/flutter_test/lib/
http:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/http-0.13.5/lib/
http_parser:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/http_parser-4.0.2/lib/
implicitly_animated_reorderable_list_2:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/implicitly_animated_reorderable_list_2-0.5.1/lib/
matcher:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.10/lib/
material_design_icons_flutter:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/material_design_icons_flutter-6.0.7296/lib/
material_floating_search_bar_2:../lib/
meta:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/meta-1.7.0/lib/
nested:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/nested-1.0.0/lib/
path:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/path-1.8.0/lib/
provider:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/provider-6.1.1/lib/
sky_engine:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/bin/cache/pkg/sky_engine/lib/
source_span:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1/lib/
stack_trace:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib/
stream_channel:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0/lib/
string_scanner:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.1.0/lib/
term_glyph:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.2.0/lib/
test_api:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/test_api-0.4.2/lib/
typed_data:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/typed_data-1.3.0/lib/
vector_math:file:///C:/24%20Rizal%20Hermawan/flutter_2.5.3/.pub-cache/hosted/pub.dartlang.org/vector_math-2.1.0/lib/
example:lib/
# example
An example project for the material design floating search bar.
# Specify analysis options.
#
# For a list of lints, see: http://dart-lang.github.io/linter/lints/
# See the configuration guide for more
# https://github.com/dart-lang/sdk/tree/main/pkg/analyzer#configuring-the-analyzer
#
# There are other similar analysis options files in the flutter repos,
# which should be kept in sync with this file:
#
# - analysis_options.yaml (this file)
# - https://github.com/flutter/plugins/blob/master/analysis_options.yaml
# - https://github.com/flutter/engine/blob/master/analysis_options.yaml
# - https://github.com/flutter/packages/blob/master/analysis_options.yaml
#
# This file contains the analysis options used for code in the flutter/flutter
# repository.
analyzer:
language:
strict-casts: true
strict-raw-types: true
errors:
# allow self-reference to deprecated members (we do this because otherwise we have
# to annotate every member in every test, assert, etc, when we deprecate something)
deprecated_member_use_from_same_package: ignore
exclude:
- "bin/cache/**"
# Ignore protoc generated files
- "dev/conductor/lib/proto/*"
linter:
rules:
# This list is derived from the list of all available lints located at
# https://github.com/dart-lang/linter/blob/master/example/all.yaml
- always_declare_return_types
- always_put_control_body_on_new_line
# - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219
- always_specify_types
# - always_use_package_imports # we do this commonly
- annotate_overrides
# - avoid_annotating_with_dynamic # conflicts with always_specify_types
- avoid_bool_literals_in_conditional_expressions
# - avoid_catches_without_on_clauses # blocked on https://github.com/dart-lang/linter/issues/3023
# - avoid_catching_errors # blocked on https://github.com/dart-lang/linter/issues/3023
# avoid_classes_with_only_static_members # # we do this commonly for `abstract final class`es
- avoid_double_and_int_checks
- avoid_dynamic_calls
- avoid_empty_else
- avoid_equals_and_hash_code_on_mutable_classes
- avoid_escaping_inner_quotes
- avoid_field_initializers_in_const_classes
# - avoid_final_parameters # incompatible with prefer_final_parameters
- avoid_function_literals_in_foreach_calls
- avoid_implementing_value_types
- avoid_init_to_null
- avoid_js_rounded_ints
# - avoid_multiple_declarations_per_line # seems to be a stylistic choice we don't subscribe to
- avoid_null_checks_in_equality_operators
# - avoid_positional_boolean_parameters # would have been nice to enable this but by now there's too many places that break it
- avoid_print
# - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356)
- avoid_redundant_argument_values
- avoid_relative_lib_imports
- avoid_renaming_method_parameters
- avoid_return_types_on_setters
- avoid_returning_null_for_void
# - avoid_returning_this # there are enough valid reasons to return `this` that this lint ends up with too many false positives
- avoid_setters_without_getters
- avoid_shadowing_type_parameters
- avoid_single_cascade_in_expression_statements
- avoid_slow_async_io
- avoid_type_to_string
- avoid_types_as_parameter_names
# - avoid_types_on_closure_parameters # conflicts with always_specify_types
- avoid_unnecessary_containers
- avoid_unused_constructor_parameters
- avoid_void_async
# - avoid_web_libraries_in_flutter # we use web libraries in web-specific code, and our tests prevent us from using them elsewhere
- await_only_futures
- camel_case_extensions
- camel_case_types
- cancel_subscriptions
# - cascade_invocations # doesn't match the typical style of this repo
- cast_nullable_to_non_nullable
# - close_sinks # not reliable enough
- collection_methods_unrelated_type
- combinators_ordering
# - comment_references # blocked on https://github.com/dart-lang/linter/issues/1142
- conditional_uri_does_not_exist
# - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204
- control_flow_in_finally
- curly_braces_in_flow_control_structures
- dangling_library_doc_comments
- depend_on_referenced_packages
- deprecated_consistency
# - diagnostic_describe_all_properties # enabled only at the framework level (packages/flutter/lib)
- directives_ordering
# - discarded_futures # too many false positives, similar to unawaited_futures
# - do_not_use_environment # there are appropriate times to use the environment, especially in our tests and build logic
- empty_catches
- empty_constructor_bodies
- empty_statements
- eol_at_end_of_file
- exhaustive_cases
- file_names
- flutter_style_todos
- hash_and_equals
- implementation_imports
- implicit_call_tearoffs
- invalid_case_patterns
- iterable_contains_unrelated_type
# - join_return_with_assignment # not required by flutter style
- leading_newlines_in_multiline_strings
- library_annotations
- library_names
- library_prefixes
- library_private_types_in_public_api
# - lines_longer_than_80_chars # not required by flutter style
- list_remove_unrelated_type
# - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/linter/issues/453
- missing_whitespace_between_adjacent_strings
- no_adjacent_strings_in_list
- no_default_cases
- no_duplicate_case_values
- no_leading_underscores_for_library_prefixes
- no_leading_underscores_for_local_identifiers
- no_logic_in_create_state
# - no_runtimeType_toString # ok in tests; we enable this only in packages/
- non_constant_identifier_names
- noop_primitive_operations
- null_check_on_nullable_type_parameter
- null_closures
# - omit_local_variable_types # opposite of always_specify_types
# - one_member_abstracts # too many false positives
- only_throw_errors # this does get disabled in a few places where we have legacy code that uses strings et al
- overridden_fields
- package_api_docs
- package_names
- package_prefixed_library_names
# - parameter_assignments # we do this commonly
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
# - prefer_asserts_with_message # not required by flutter style
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
# - prefer_constructors_over_static_methods # far too many false positives
- prefer_contains
# - prefer_double_quotes # opposite of prefer_single_quotes
# - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
- prefer_final_fields
- prefer_final_in_for_each
- prefer_final_locals
# - prefer_final_parameters # we should enable this one day when it can be auto-fixed (https://github.com/dart-lang/linter/issues/3104), see also parameter_assignments
- prefer_for_elements_to_map_fromIterable
- prefer_foreach
- prefer_function_declarations_over_variables
- prefer_generic_function_type_aliases
- prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
# - prefer_int_literals # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#use-double-literals-for-double-constants
- prefer_interpolation_to_compose_strings
- prefer_is_empty
- prefer_is_not_empty
- prefer_is_not_operator
- prefer_iterable_whereType
- prefer_mixin
# - prefer_null_aware_method_calls # "call()" is confusing to people new to the language since it's not documented anywhere
- prefer_null_aware_operators
- prefer_relative_imports
- prefer_single_quotes
- prefer_spread_collections
- prefer_typing_uninitialized_variables
- prefer_void_to_null
- provide_deprecation_message
# - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml
- recursive_getters
# - require_trailing_commas # would be nice, but requires a lot of manual work: 10,000+ code locations would need to be reformatted by hand after bulk fix is applied
- secure_pubspec_urls
- sized_box_for_whitespace
- sized_box_shrink_expand
- slash_for_doc_comments
- sort_child_properties_last
- sort_constructors_first
# - sort_pub_dependencies # prevents separating pinned transitive dependencies
- sort_unnamed_constructors_first
- test_types_in_equals
- throw_in_finally
- tighten_type_of_initializing_formals
# - type_annotate_public_apis # subset of always_specify_types
- type_init_formals
# - unawaited_futures # too many false positives, especially with the way AnimationController works
- unnecessary_await_in_return
- unnecessary_brace_in_string_interps
- unnecessary_breaks
- unnecessary_const
- unnecessary_constructor_name
# - unnecessary_final # conflicts with prefer_final_locals
- unnecessary_getters_setters
# - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498
- unnecessary_late
- unnecessary_library_directive
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_aware_operator_on_extension_on_nullable
- unnecessary_null_checks
- unnecessary_null_in_if_null_operators
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_overrides
- unnecessary_parenthesis
# - unnecessary_raw_strings # what's "necessary" is a matter of opinion; consistency across strings can help readability more than this lint
- unnecessary_statements
- unnecessary_string_escapes
- unnecessary_string_interpolations
- unnecessary_this
- unnecessary_to_list_in_spreads
# - unreachable_from_main # Do not enable this rule until it is un-marked as "experimental" and carefully re-evaluated.
- unrelated_type_equality_checks
- unsafe_html
- use_build_context_synchronously
- use_colored_box
# - use_decorated_box # leads to bugs: DecoratedBox and Container are not equivalent (Container inserts extra padding)
- use_enums
- use_full_hex_values_for_flutter_colors
- use_function_type_syntax_for_parameters
- use_if_null_to_convert_nulls_to_bools
- use_is_even_rather_than_modulo
- use_key_in_widget_constructors
- use_late_for_private_fields_and_variables
- use_named_constants
- use_raw_strings
- use_rethrow_when_possible
- use_setters_to_change_properties
# - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182
- use_string_in_part_of_directives
- use_super_parameters
- use_test_throws_matchers
# - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
- valid_regexps
- void_checks
\ No newline at end of file
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
namespace "com.example.example"
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.example"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
package io.flutter.plugins;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import io.flutter.Log;
import io.flutter.embedding.engine.FlutterEngine;
/**
* Generated file. Do not edit.
* This file is generated by the Flutter tool based on the
* plugins that support the Android platform.
*/
@Keep
public final class GeneratedPluginRegistrant {
private static final String TAG = "GeneratedPluginRegistrant";
public static void registerWith(@NonNull FlutterEngine flutterEngine) {
}
}
package com.example.example
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
buildscript {
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
sdk.dir=C:\\Users\\asset\\AppData\\Local\\Android\\sdk
flutter.sdk=C:\\24 Rizal Hermawan\\flutter_2.5.3
\ No newline at end of file
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
</dict>
</plist>
#include "Generated.xcconfig"
// This is a generated file; do not edit or check into version control.
FLUTTER_ROOT=C:\24 Rizal Hermawan\flutter_2.5.3
FLUTTER_APPLICATION_PATH=C:\24 Rizal Hermawan\job\mobile\codingan\edit_pub\material_floating_search_bar_2-0.5.0\example
COCOAPODS_PARALLEL_CODE_SIGN=true
FLUTTER_TARGET=lib\main.dart
FLUTTER_BUILD_DIR=build
FLUTTER_BUILD_NAME=1.0.0
FLUTTER_BUILD_NUMBER=1
EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386
DART_OBFUSCATION=false
TRACK_WIDGET_CREATION=false
TREE_SHAKE_ICONS=false
PACKAGE_CONFIG=.packages
#include "Generated.xcconfig"
#!/bin/sh
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=C:\24 Rizal Hermawan\flutter_2.5.3"
export "FLUTTER_APPLICATION_PATH=C:\24 Rizal Hermawan\job\mobile\codingan\edit_pub\material_floating_search_bar_2-0.5.0\example"
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
export "FLUTTER_TARGET=lib\main.dart"
export "FLUTTER_BUILD_DIR=build"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1"
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=false"
export "TREE_SHAKE_ICONS=false"
export "PACKAGE_CONFIG=.packages"
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
remoteInfo = Runner;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C807B294A618700263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
331C8080294A63A400263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
331C807D294A63A400263BE5 /* Sources */,
331C807E294A63A400263BE5 /* Frameworks */,
331C807F294A63A400263BE5 /* Resources */,
);
buildRules = (
);
dependencies = (
331C8086294A63A400263BE5 /* PBXTargetDependency */,
);
name = RunnerTests;
productName = RunnerTests;
productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 97C146ED1CF9000F007C117D;
};
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
331C8080294A63A400263BE5 /* RunnerTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
331C807F294A63A400263BE5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
331C807D294A63A400263BE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = AE0B7B92F70575B8D7E0D07E /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Debug;
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 89B67EB44CE7B6631473024E /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Release;
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 640959BDD8F10B91D80A66BE /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C8088294A63A400263BE5 /* Debug */,
331C8089294A63A400263BE5 /* Release */,
331C808A294A63A400263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C8080294A63A400263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GeneratedPluginRegistrant_h
#define GeneratedPluginRegistrant_h
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
@interface GeneratedPluginRegistrant : NSObject
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry;
@end
NS_ASSUME_NONNULL_END
#endif /* GeneratedPluginRegistrant_h */
//
// Generated file. Do not edit.
//
// clang-format off
#import "GeneratedPluginRegistrant.h"
@implementation GeneratedPluginRegistrant
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
}
@end
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Example</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>example</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>
#import "GeneratedPluginRegistrant.h"
import Flutter
import UIKit
import XCTest
class RunnerTests: XCTestCase {
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}
import 'package:flutter/material.dart';
import 'package:material_floating_search_bar_2/material_floating_search_bar_2.dart';
class Example extends StatefulWidget {
const Example({super.key});
@override
State<Example> createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
@override
Widget build(BuildContext context) {
return Scaffold(
// This is handled by the search bar itself.
resizeToAvoidBottomInset: false,
body: Stack(
fit: StackFit.expand,
children: <Widget>[
// buildMap(),
// buildBottomNavigationBar(),
buildFloatingSearchBar(),
],
),
);
}
Widget buildFloatingSearchBar() {
final bool isPortrait =
MediaQuery.of(context).orientation == Orientation.portrait;
return FloatingSearchBar(
scrollPadding: const EdgeInsets.only(top: 16, bottom: 56),
transitionDuration: const Duration(milliseconds: 800),
transitionCurve: Curves.easeInOut,
physics: const BouncingScrollPhysics(),
axisAlignment: isPortrait ? 0.0 : -1.0,
openAxisAlignment: 0.0,
width: isPortrait ? 600 : 500,
debounceDelay: const Duration(milliseconds: 500),
onQueryChanged: (String query) {
// Call your model, bloc, controller here.
},
// Specify a custom transition to be used for
// animating between opened and closed stated.
transition: CircularFloatingSearchBarTransition(),
actions: <Widget>[
FloatingSearchBarAction(
child: CircularButton(
icon: const Icon(Icons.place),
onPressed: () {},
),
),
FloatingSearchBarAction.searchToClear(
showIfClosed: false,
),
],
builder: (BuildContext context, Animation<double> transition) {
return ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Material(
color: Colors.white,
elevation: 4.0,
child: Column(
mainAxisSize: MainAxisSize.min,
children: Colors.accents.map((MaterialAccentColor color) {
return Container(height: 112, color: color);
}).toList(),
),
),
);
},
);
}
}
/* class ScrollingExample extends StatelessWidget {
const ScrollingExample({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// ignore: missing_required_param
return FloatingSearchBar(
// Your pages or just a simple Scaffold...
body: IndexedStack(
children: [
MyAwesomePage(),
],
),
);
}
} */
class MyAwesomePage extends StatelessWidget {
const MyAwesomePage({super.key});
@override
Widget build(BuildContext context) {
/// Wrap you Scrollable in a FloatingSearchBarScrollNotifier
/// to indicate that the FloatingSearchBar should react to
/// scroll events from this Scrollable.
return FloatingSearchBarScrollNotifier(
child: SingleChildScrollView(
child: Container(
height: 500,
),
),
);
}
}
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:implicitly_animated_reorderable_list_2/implicitly_animated_reorderable_list_2.dart';
import 'package:implicitly_animated_reorderable_list_2/transitions.dart';
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:material_floating_search_bar_2/material_floating_search_bar_2.dart';
import 'package:provider/provider.dart';
import 'place.dart';
import 'search_model.dart';
void main() {
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
systemNavigationBarColor: Colors.white,
),
);
runApp(
MaterialApp(
title: 'Material Floating Search Bar Example',
debugShowCheckedModeBanner: false,
theme: ThemeData.light().copyWith(
iconTheme: const IconThemeData(
color: Color(0xFF4d4d4d),
),
floatingActionButtonTheme: const FloatingActionButtonThemeData(
elevation: 4,
),
),
home: Directionality(
textDirection: TextDirection.ltr,
child: ChangeNotifierProvider<SearchModel>(
create: (_) => SearchModel(),
child: const Home(),
),
),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Home(),
);
}
}
class Home extends StatefulWidget {
const Home({super.key});
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
final FloatingSearchBarController controller = FloatingSearchBarController();
int _index = 0;
int get index => _index;
set index(int value) {
_index = min(value, 2);
_index == 2 ? controller.hide() : controller.show();
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
drawer: Drawer(
child: Container(
width: 200,
),
),
body: buildSearchBar(),
);
}
Widget buildSearchBar() {
final List<FloatingSearchBarAction> actions = <FloatingSearchBarAction>[
FloatingSearchBarAction(
child: CircularButton(
icon: const Icon(Icons.place),
onPressed: () {},
),
),
FloatingSearchBarAction.searchToClear(
showIfClosed: false,
),
];
final bool isPortrait =
MediaQuery.of(context).orientation == Orientation.portrait;
return Consumer<SearchModel>(
builder: (BuildContext context, SearchModel model, _) =>
FloatingSearchBar(
automaticallyImplyBackButton: false,
controller: controller,
hint: 'חיפוש...',
iconColor: Colors.grey,
transitionDuration: const Duration(milliseconds: 800),
transitionCurve: Curves.easeInOutCubic,
physics: const BouncingScrollPhysics(),
axisAlignment: isPortrait ? 0.0 : -1.0,
openAxisAlignment: 0.0,
actions: actions,
progress: model.isLoading,
debounceDelay: const Duration(milliseconds: 500),
onQueryChanged: model.onQueryChanged,
onKeyEvent: (KeyEvent keyEvent) {
if (keyEvent.logicalKey == LogicalKeyboardKey.escape) {
controller.query = '';
controller.close();
}
},
scrollPadding: EdgeInsets.zero,
transition: CircularFloatingSearchBarTransition(spacing: 16),
builder: (BuildContext context, _) => buildExpandableBody(model),
body: buildBody(),
),
);
}
Widget buildBody() {
return Column(
children: <Widget>[
Expanded(
child: IndexedStack(
index: min(index, 2),
children: const <Widget>[
Map(),
SomeScrollableContent(),
FloatingSearchAppBarExample(),
],
),
),
buildBottomNavigationBar(),
],
);
}
Widget buildExpandableBody(SearchModel model) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Material(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
clipBehavior: Clip.antiAlias,
child: ImplicitlyAnimatedList<Place>(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
items: model.suggestions,
insertDuration: const Duration(milliseconds: 700),
itemBuilder: (BuildContext context, Animation<double> animation,
Place item, _) {
return SizeFadeTransition(
animation: animation,
child: buildItem(context, item),
);
},
updateItemBuilder:
(BuildContext context, Animation<double> animation, Place item) {
return FadeTransition(
opacity: animation,
child: buildItem(context, item),
);
},
areItemsTheSame: (Place a, Place b) => a == b,
),
),
);
}
Widget buildItem(BuildContext context, Place place) {
final ThemeData theme = Theme.of(context);
final TextTheme textTheme = theme.textTheme;
final SearchModel model = Provider.of<SearchModel>(context, listen: false);
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
InkWell(
onTap: () {
FloatingSearchBar.of(context)?.close();
Future<void>.delayed(
const Duration(milliseconds: 500),
() => model.clear(),
);
},
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: <Widget>[
SizedBox(
width: 36,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
child: model.suggestions == history
? const Icon(Icons.history, key: Key('history'))
: const Icon(Icons.place, key: Key('place')),
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
place.name,
style: textTheme.titleMedium,
),
const SizedBox(height: 2),
Text(
place.level2Address,
style: textTheme.bodyMedium
?.copyWith(color: Colors.grey.shade600),
),
],
),
),
],
),
),
),
if (model.suggestions.isNotEmpty && place != model.suggestions.last)
const Divider(height: 0),
],
);
}
Widget buildBottomNavigationBar() {
return BottomNavigationBar(
onTap: (int value) => index = value,
currentIndex: index,
elevation: 16,
type: BottomNavigationBarType.fixed,
showUnselectedLabels: true,
backgroundColor: Colors.white,
selectedItemColor: Colors.blue,
selectedFontSize: 11.5,
unselectedFontSize: 11.5,
unselectedItemColor: const Color(0xFF4d4d4d),
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(MdiIcons.homeVariantOutline),
label: 'Explore',
),
BottomNavigationBarItem(
icon: Icon(MdiIcons.homeCityOutline),
label: 'Commute',
),
BottomNavigationBarItem(
icon: Icon(MdiIcons.bookmarkOutline),
label: 'Saved',
),
BottomNavigationBarItem(
icon: Icon(MdiIcons.plusCircleOutline),
label: 'Contribute',
),
BottomNavigationBarItem(
icon: Icon(MdiIcons.bellOutline),
label: 'Updates',
),
],
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
class Map extends StatelessWidget {
const Map({super.key});
@override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: <Widget>[
buildMap(),
buildFabs(),
],
);
}
Widget buildFabs() {
return Align(
alignment: AlignmentDirectional.bottomEnd,
child: Padding(
padding: const EdgeInsetsDirectional.only(bottom: 16, end: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Builder(
builder: (BuildContext context) => FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute<dynamic>(
builder: (BuildContext context) => const SearchBar(),
),
);
},
backgroundColor: Colors.white,
child: const Icon(Icons.gps_fixed, color: Color(0xFF4d4d4d)),
),
),
const SizedBox(height: 16),
FloatingActionButton(
onPressed: () {},
heroTag: 'öslkföl',
backgroundColor: Colors.blue,
child: const Icon(Icons.directions),
),
],
),
),
);
}
Widget buildMap() {
return Image.asset(
'assets/map.jpg',
fit: BoxFit.cover,
);
}
}
class SearchBar extends StatefulWidget {
const SearchBar({
super.key,
});
@override
State<SearchBar> createState() => _SearchBarState();
}
class _SearchBarState extends State<SearchBar> {
final FloatingSearchBarController controller = FloatingSearchBarController();
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: FloatingSearchBar(
contextMenuBuilder:
(BuildContext context, EditableTextState editableTextState) {
final List<ContextMenuButtonItem> buttonItems =
editableTextState.contextMenuButtonItems;
return AdaptiveTextSelectionToolbar.buttonItems(
anchors: editableTextState.contextMenuAnchors,
buttonItems: buttonItems,
);
},
controller: controller,
title: const Text(
'Aschaffenburg',
),
hint: 'Search for a place',
builder: (BuildContext context, _) {
return Container();
},
),
);
}
}
class SomeScrollableContent extends StatelessWidget {
const SomeScrollableContent({super.key});
@override
Widget build(BuildContext context) {
return FloatingSearchBarScrollNotifier(
child: ListView.separated(
padding: const EdgeInsets.only(top: kToolbarHeight),
itemCount: 100,
separatorBuilder: (BuildContext context, int index) => const Divider(),
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text('Item $index'),
);
},
),
);
}
}
class FloatingSearchAppBarExample extends StatelessWidget {
const FloatingSearchAppBarExample({super.key});
@override
Widget build(BuildContext context) {
return FloatingSearchAppBar(
title: const Text('Title'),
transitionDuration: const Duration(milliseconds: 800),
color: Colors.greenAccent.shade100,
colorOnScroll: Colors.greenAccent.shade200,
body: ListView.separated(
padding: EdgeInsets.zero,
itemCount: 100,
separatorBuilder: (BuildContext context, int index) => const Divider(),
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text('Item $index'),
);
},
),
);
}
}
// ignore_for_file: avoid_equals_and_hash_code_on_mutable_classes
class Place {
const Place({
required this.name,
this.state = '',
required this.country,
});
factory Place.fromJson(Map<String, dynamic> map) {
final Map<String, dynamic> props =
map['properties']! as Map<String, dynamic>;
return Place(
name: props['name'] as String? ?? '',
state: props['state'] as String? ?? '',
country: props['country'] as String? ?? '',
);
}
final String name;
final String state;
final String country;
bool get hasState => state.isNotEmpty == true;
bool get hasCountry => country.isNotEmpty == true;
bool get isCountry => hasCountry && name == country;
bool get isState => hasState && name == state;
String get address {
if (isCountry) {
return country;
}
return '$name, $level2Address';
}
String get addressShort {
if (isCountry) {
return country;
}
return '$name, $country';
}
String get level2Address {
if (isCountry || isState || !hasState) {
return country;
}
if (!hasCountry) {
return state;
}
return '$state, $country';
}
@override
String toString() => 'Place(name: $name, state: $state, country: $country)';
@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
return other is Place &&
other.name == name &&
other.state == state &&
other.country == country;
}
@override
int get hashCode => name.hashCode ^ state.hashCode ^ country.hashCode;
}
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'place.dart';
class SearchModel extends ChangeNotifier {
bool _isLoading = false;
bool get isLoading => _isLoading;
List<Place> _suggestions = history;
List<Place> get suggestions => _suggestions;
String _query = '';
String get query => _query;
Future<void> onQueryChanged(String query) async {
if (query == _query) {
return;
}
_query = query;
_isLoading = true;
notifyListeners();
if (query.isEmpty) {
_suggestions = history;
} else {
final http.Response response =
await http.get(Uri.parse('https://photon.komoot.io/api/?q=$query'));
final dynamic body = json.decode(utf8.decode(response.bodyBytes));
// ignore: avoid_dynamic_calls
final List<dynamic> features = body['features'] as List<dynamic>;
_suggestions = features
.map((dynamic e) => Place.fromJson(e as Map<String, dynamic>))
.toSet()
.toList();
}
_isLoading = false;
notifyListeners();
}
void clear() {
_suggestions = history;
notifyListeners();
}
}
const List<Place> history = <Place>[
Place(
name: 'San Fracisco',
country: 'United States of America',
state: 'California',
),
Place(
name: 'Singapore',
country: 'Singapore',
),
Place(
name: 'Munich',
state: 'Bavaria',
country: 'Germany',
),
Place(
name: 'London',
country: 'United Kingdom',
),
];
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
http:
dependency: "direct main"
description:
name: http
url: "https://pub.dartlang.org"
source: hosted
version: "0.13.5"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.2"
implicitly_animated_reorderable_list_2:
dependency: "direct main"
description:
name: implicitly_animated_reorderable_list_2
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.1"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10"
material_design_icons_flutter:
dependency: "direct main"
description:
name: material_design_icons_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.7296"
material_floating_search_bar_2:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "0.5.0"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
nested:
dependency: transitive
description:
name: nested
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
provider:
dependency: "direct main"
description:
name: provider
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.1"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.2"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
sdks:
dart: ">=2.14.0 <3.0.0"
flutter: ">=1.16.0"
name: example
description: Example project for the material_floating_search_bar
version: 1.0.0+1
publish_to: none
environment:
sdk: ">=2.12.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
http: ^0.13.5
implicitly_animated_reorderable_list_2: ^0.5.1
material_design_icons_flutter: ^6.0.7096
material_floating_search_bar_2:
path: ../
provider: ^6.0.5
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
assets:
- assets/map.jpg
fonts:
- family: Montserrat
fonts:
- asset: assets/fonts/Montserrat/Montserrat-SemiBold.ttf
weight: 500
- asset: assets/fonts/Montserrat/Montserrat-ExtraBold.ttf
weight: 600
- asset: assets/fonts/Montserrat/Montserrat-Black.ttf
weight: 700
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="example">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>example</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
var serviceWorkerVersion = null;
var scriptLoaded = false;
function loadMainDartJs() {
if (scriptLoaded) {
return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
document.body.append(scriptTag);
}
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl)
.then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
}
});
}
if (!reg.active && (reg.installing || reg.waiting)) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing || reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.',
);
loadMainDartJs();
}
}, 4000);
});
} else {
// Service workers not supported. Just drop the <script> tag.
loadMainDartJs();
}
</script>
</body>
</html>
{
"name": "example",
"short_name": "example",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
cmake_minimum_required(VERSION 3.14)
project(example LANGUAGES CXX)
set(BINARY_NAME "example")
cmake_policy(SET CMP0063 NEW)
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Configure build options.
get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(IS_MULTICONFIG)
set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
CACHE STRING "" FORCE)
else()
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()
endif()
set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
# Use Unicode for all projects.
add_definitions(-DUNICODE -D_UNICODE)
# Compilation settings that should be applied to most targets.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_17)
target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
target_compile_options(${TARGET} PRIVATE /EHsc)
target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
target_compile_definitions(${TARGET} PRIVATE "$<$<CONFIG:Debug>:_DEBUG>")
endfunction()
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
# Flutter library and tool build rules.
add_subdirectory(${FLUTTER_MANAGED_DIR})
# Application build
add_subdirectory("runner")
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
# === Installation ===
# Support files are copied into place next to the executable, so that it can
# run in place. This is done instead of making a separate bundle (as on Linux)
# so that building and running from within Visual Studio will work.
set(BUILD_BUNDLE_DIR "$<TARGET_FILE_DIR:${BINARY_NAME}>")
# Make the "install" step default, as it's required to run.
set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
if(PLUGIN_BUNDLED_LIBRARIES)
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
# Install the AOT library on non-Debug builds only.
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
CONFIGURATIONS Profile;Release
COMPONENT Runtime)
cmake_minimum_required(VERSION 3.14)
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
# Configuration provided via flutter tool.
include(${EPHEMERAL_DIR}/generated_config.cmake)
# TODO: Move the rest of this into files in ephemeral. See
# https://github.com/flutter/flutter/issues/57146.
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
# === Flutter Library ===
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
# Published to parent scope for install step.
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
list(APPEND FLUTTER_LIBRARY_HEADERS
"flutter_export.h"
"flutter_windows.h"
"flutter_messenger.h"
"flutter_plugin_registrar.h"
"flutter_texture_registrar.h"
)
list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
add_library(flutter INTERFACE)
target_include_directories(flutter INTERFACE
"${EPHEMERAL_DIR}"
)
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
add_dependencies(flutter flutter_assemble)
# === Wrapper ===
list(APPEND CPP_WRAPPER_SOURCES_CORE
"core_implementations.cc"
"standard_codec.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
"plugin_registrar.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
list(APPEND CPP_WRAPPER_SOURCES_APP
"flutter_engine.cc"
"flutter_view_controller.cc"
)
list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
# Wrapper sources needed for a plugin.
add_library(flutter_wrapper_plugin STATIC
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
)
apply_standard_settings(flutter_wrapper_plugin)
set_target_properties(flutter_wrapper_plugin PROPERTIES
POSITION_INDEPENDENT_CODE ON)
set_target_properties(flutter_wrapper_plugin PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
target_include_directories(flutter_wrapper_plugin PUBLIC
"${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_plugin flutter_assemble)
# Wrapper sources needed for the runner.
add_library(flutter_wrapper_app STATIC
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_APP}
)
apply_standard_settings(flutter_wrapper_app)
target_link_libraries(flutter_wrapper_app PUBLIC flutter)
target_include_directories(flutter_wrapper_app PUBLIC
"${WRAPPER_ROOT}/include"
)
add_dependencies(flutter_wrapper_app flutter_assemble)
# === Flutter tool backend ===
# _phony_ is a non-existent file to force this command to run every time,
# since currently there's no way to get a full input/output list from the
# flutter tool.
set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
add_custom_command(
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
${PHONY_OUTPUT}
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
windows-x64 $<CONFIG>
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
"${FLUTTER_LIBRARY}"
${FLUTTER_LIBRARY_HEADERS}
${CPP_WRAPPER_SOURCES_CORE}
${CPP_WRAPPER_SOURCES_PLUGIN}
${CPP_WRAPPER_SOURCES_APP}
)
//
// Generated file. Do not edit.
//
// clang-format off
#include "generated_plugin_registrant.h"
void RegisterPlugins(flutter::PluginRegistry* registry) {
}
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_
#include <flutter/plugin_registry.h>
// Registers Flutter plugins.
void RegisterPlugins(flutter::PluginRegistry* registry);
#endif // GENERATED_PLUGIN_REGISTRANT_
#
# Generated file, do not edit.
#
list(APPEND FLUTTER_PLUGIN_LIST
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
)
set(PLUGIN_BUNDLED_LIBRARIES)
foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)
cmake_minimum_required(VERSION 3.14)
project(runner LANGUAGES CXX)
add_executable(${BINARY_NAME} WIN32
"flutter_window.cpp"
"main.cpp"
"utils.cpp"
"win32_window.cpp"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
"Runner.rc"
"runner.exe.manifest"
)
apply_standard_settings(${BINARY_NAME})
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
add_dependencies(${BINARY_NAME} flutter_assemble)
// Microsoft Visual C++ generated resource script.
//
#pragma code_page(65001)
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_APP_ICON ICON "resources\\app_icon.ico"
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
#ifdef FLUTTER_BUILD_NUMBER
#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
#else
#define VERSION_AS_NUMBER 1,0,0
#endif
#ifdef FLUTTER_BUILD_NAME
#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
#else
#define VERSION_AS_STRING "1.0.0"
#endif
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_AS_NUMBER
PRODUCTVERSION VERSION_AS_NUMBER
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "com.example" "\0"
VALUE "FileDescription", "example" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "example" "\0"
VALUE "LegalCopyright", "Copyright (C) 2022 com.example. All rights reserved." "\0"
VALUE "OriginalFilename", "example.exe" "\0"
VALUE "ProductName", "example" "\0"
VALUE "ProductVersion", VERSION_AS_STRING "\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
#include "flutter_window.h"
#include <optional>
#include "flutter/generated_plugin_registrant.h"
FlutterWindow::FlutterWindow(const flutter::DartProject& project)
: project_(project) {}
FlutterWindow::~FlutterWindow() {}
bool FlutterWindow::OnCreate() {
if (!Win32Window::OnCreate()) {
return false;
}
RECT frame = GetClientArea();
// The size here must match the window dimensions to avoid unnecessary surface
// creation / destruction in the startup path.
flutter_controller_ = std::make_unique<flutter::FlutterViewController>(
frame.right - frame.left, frame.bottom - frame.top, project_);
// Ensure that basic setup of the controller was successful.
if (!flutter_controller_->engine() || !flutter_controller_->view()) {
return false;
}
RegisterPlugins(flutter_controller_->engine());
SetChildContent(flutter_controller_->view()->GetNativeWindow());
return true;
}
void FlutterWindow::OnDestroy() {
if (flutter_controller_) {
flutter_controller_ = nullptr;
}
Win32Window::OnDestroy();
}
LRESULT
FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
// Give Flutter, including plugins, an opportunity to handle window messages.
if (flutter_controller_) {
std::optional<LRESULT> result =
flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
lparam);
if (result) {
return *result;
}
}
switch (message) {
case WM_FONTCHANGE:
flutter_controller_->engine()->ReloadSystemFonts();
break;
}
return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
}
#ifndef RUNNER_FLUTTER_WINDOW_H_
#define RUNNER_FLUTTER_WINDOW_H_
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <memory>
#include "win32_window.h"
// A window that does nothing but host a Flutter view.
class FlutterWindow : public Win32Window {
public:
// Creates a new FlutterWindow hosting a Flutter view running |project|.
explicit FlutterWindow(const flutter::DartProject& project);
virtual ~FlutterWindow();
protected:
// Win32Window:
bool OnCreate() override;
void OnDestroy() override;
LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
LPARAM const lparam) noexcept override;
private:
// The project to run.
flutter::DartProject project_;
// The Flutter instance hosted by this window.
std::unique_ptr<flutter::FlutterViewController> flutter_controller_;
};
#endif // RUNNER_FLUTTER_WINDOW_H_
#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <windows.h>
#include "flutter_window.h"
#include "utils.h"
int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
_In_ wchar_t *command_line, _In_ int show_command) {
// Attach to console when present (e.g., 'flutter run') or create a
// new console when running with a debugger.
if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
CreateAndAttachConsole();
}
// Initialize COM, so that it is available for use in the library and/or
// plugins.
::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
flutter::DartProject project(L"data");
std::vector<std::string> command_line_arguments =
GetCommandLineArguments();
project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
FlutterWindow window(project);
Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720);
if (!window.CreateAndShow(L"example", origin, size)) {
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);
::MSG msg;
while (::GetMessage(&msg, nullptr, 0, 0)) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
::CoUninitialize();
return EXIT_SUCCESS;
}
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Runner.rc
//
#define IDI_APP_ICON 101
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
</assembly>
#include "utils.h"
#include <flutter_windows.h>
#include <io.h>
#include <stdio.h>
#include <windows.h>
#include <iostream>
void CreateAndAttachConsole() {
if (::AllocConsole()) {
FILE *unused;
if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
_dup2(_fileno(stdout), 1);
}
if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
_dup2(_fileno(stdout), 2);
}
std::ios::sync_with_stdio();
FlutterDesktopResyncOutputStreams();
}
}
std::vector<std::string> GetCommandLineArguments() {
// Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
int argc;
wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
if (argv == nullptr) {
return std::vector<std::string>();
}
std::vector<std::string> command_line_arguments;
// Skip the first argument as it's the binary name.
for (int i = 1; i < argc; i++) {
command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
}
::LocalFree(argv);
return command_line_arguments;
}
std::string Utf8FromUtf16(const wchar_t* utf16_string) {
if (utf16_string == nullptr) {
return std::string();
}
int target_length = ::WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
-1, nullptr, 0, nullptr, nullptr);
if (target_length == 0) {
return std::string();
}
std::string utf8_string;
utf8_string.resize(target_length);
int converted_length = ::WideCharToMultiByte(
CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,
-1, utf8_string.data(),
target_length, nullptr, nullptr);
if (converted_length == 0) {
return std::string();
}
return utf8_string;
}
#ifndef RUNNER_UTILS_H_
#define RUNNER_UTILS_H_
#include <string>
#include <vector>
// Creates a console for the process, and redirects stdout and stderr to
// it for both the runner and the Flutter library.
void CreateAndAttachConsole();
// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
// encoded in UTF-8. Returns an empty std::string on failure.
std::string Utf8FromUtf16(const wchar_t* utf16_string);
// Gets the command line arguments passed in as a std::vector<std::string>,
// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.
std::vector<std::string> GetCommandLineArguments();
#endif // RUNNER_UTILS_H_
#include "win32_window.h"
#include <flutter_windows.h>
#include "resource.h"
namespace {
constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
// The number of Win32Window objects that currently exist.
static int g_active_window_count = 0;
using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
// Scale helper to convert logical scaler values to physical using passed in
// scale factor
int Scale(int source, double scale_factor) {
return static_cast<int>(source * scale_factor);
}
// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
// This API is only needed for PerMonitor V1 awareness mode.
void EnableFullDpiSupportIfAvailable(HWND hwnd) {
HMODULE user32_module = LoadLibraryA("User32.dll");
if (!user32_module) {
return;
}
auto enable_non_client_dpi_scaling =
reinterpret_cast<EnableNonClientDpiScaling*>(
GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
if (enable_non_client_dpi_scaling != nullptr) {
enable_non_client_dpi_scaling(hwnd);
FreeLibrary(user32_module);
}
}
} // namespace
// Manages the Win32Window's window class registration.
class WindowClassRegistrar {
public:
~WindowClassRegistrar() = default;
// Returns the singleton registar instance.
static WindowClassRegistrar* GetInstance() {
if (!instance_) {
instance_ = new WindowClassRegistrar();
}
return instance_;
}
// Returns the name of the window class, registering the class if it hasn't
// previously been registered.
const wchar_t* GetWindowClass();
// Unregisters the window class. Should only be called if there are no
// instances of the window.
void UnregisterWindowClass();
private:
WindowClassRegistrar() = default;
static WindowClassRegistrar* instance_;
bool class_registered_ = false;
};
WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
const wchar_t* WindowClassRegistrar::GetWindowClass() {
if (!class_registered_) {
WNDCLASS window_class{};
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
window_class.lpszClassName = kWindowClassName;
window_class.style = CS_HREDRAW | CS_VREDRAW;
window_class.cbClsExtra = 0;
window_class.cbWndExtra = 0;
window_class.hInstance = GetModuleHandle(nullptr);
window_class.hIcon =
LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
window_class.hbrBackground = 0;
window_class.lpszMenuName = nullptr;
window_class.lpfnWndProc = Win32Window::WndProc;
RegisterClass(&window_class);
class_registered_ = true;
}
return kWindowClassName;
}
void WindowClassRegistrar::UnregisterWindowClass() {
UnregisterClass(kWindowClassName, nullptr);
class_registered_ = false;
}
Win32Window::Win32Window() {
++g_active_window_count;
}
Win32Window::~Win32Window() {
--g_active_window_count;
Destroy();
}
bool Win32Window::CreateAndShow(const std::wstring& title,
const Point& origin,
const Size& size) {
Destroy();
const wchar_t* window_class =
WindowClassRegistrar::GetInstance()->GetWindowClass();
const POINT target_point = {static_cast<LONG>(origin.x),
static_cast<LONG>(origin.y)};
HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
double scale_factor = dpi / 96.0;
HWND window = CreateWindow(
window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
Scale(size.width, scale_factor), Scale(size.height, scale_factor),
nullptr, nullptr, GetModuleHandle(nullptr), this);
if (!window) {
return false;
}
return OnCreate();
}
// static
LRESULT CALLBACK Win32Window::WndProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
if (message == WM_NCCREATE) {
auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
SetWindowLongPtr(window, GWLP_USERDATA,
reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));
auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);
EnableFullDpiSupportIfAvailable(window);
that->window_handle_ = window;
} else if (Win32Window* that = GetThisFromHandle(window)) {
return that->MessageHandler(window, message, wparam, lparam);
}
return DefWindowProc(window, message, wparam, lparam);
}
LRESULT
Win32Window::MessageHandler(HWND hwnd,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept {
switch (message) {
case WM_DESTROY:
window_handle_ = nullptr;
Destroy();
if (quit_on_close_) {
PostQuitMessage(0);
}
return 0;
case WM_DPICHANGED: {
auto newRectSize = reinterpret_cast<RECT*>(lparam);
LONG newWidth = newRectSize->right - newRectSize->left;
LONG newHeight = newRectSize->bottom - newRectSize->top;
SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
return 0;
}
case WM_SIZE: {
RECT rect = GetClientArea();
if (child_content_ != nullptr) {
// Size and position the child window.
MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
rect.bottom - rect.top, TRUE);
}
return 0;
}
case WM_ACTIVATE:
if (child_content_ != nullptr) {
SetFocus(child_content_);
}
return 0;
}
return DefWindowProc(window_handle_, message, wparam, lparam);
}
void Win32Window::Destroy() {
OnDestroy();
if (window_handle_) {
DestroyWindow(window_handle_);
window_handle_ = nullptr;
}
if (g_active_window_count == 0) {
WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
}
}
Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
return reinterpret_cast<Win32Window*>(
GetWindowLongPtr(window, GWLP_USERDATA));
}
void Win32Window::SetChildContent(HWND content) {
child_content_ = content;
SetParent(content, window_handle_);
RECT frame = GetClientArea();
MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
frame.bottom - frame.top, true);
SetFocus(child_content_);
}
RECT Win32Window::GetClientArea() {
RECT frame;
GetClientRect(window_handle_, &frame);
return frame;
}
HWND Win32Window::GetHandle() {
return window_handle_;
}
void Win32Window::SetQuitOnClose(bool quit_on_close) {
quit_on_close_ = quit_on_close;
}
bool Win32Window::OnCreate() {
// No-op; provided for subclasses.
return true;
}
void Win32Window::OnDestroy() {
// No-op; provided for subclasses.
}
#ifndef RUNNER_WIN32_WINDOW_H_
#define RUNNER_WIN32_WINDOW_H_
#include <windows.h>
#include <functional>
#include <memory>
#include <string>
// A class abstraction for a high DPI-aware Win32 Window. Intended to be
// inherited from by classes that wish to specialize with custom
// rendering and input handling
class Win32Window {
public:
struct Point {
unsigned int x;
unsigned int y;
Point(unsigned int x, unsigned int y) : x(x), y(y) {}
};
struct Size {
unsigned int width;
unsigned int height;
Size(unsigned int width, unsigned int height)
: width(width), height(height) {}
};
Win32Window();
virtual ~Win32Window();
// Creates and shows a win32 window with |title| and position and size using
// |origin| and |size|. New windows are created on the default monitor. Window
// sizes are specified to the OS in physical pixels, hence to ensure a
// consistent size to will treat the width height passed in to this function
// as logical pixels and scale to appropriate for the default monitor. Returns
// true if the window was created successfully.
bool CreateAndShow(const std::wstring& title,
const Point& origin,
const Size& size);
// Release OS resources associated with window.
void Destroy();
// Inserts |content| into the window tree.
void SetChildContent(HWND content);
// Returns the backing Window handle to enable clients to set icon and other
// window properties. Returns nullptr if the window has been destroyed.
HWND GetHandle();
// If true, closing this window will quit the application.
void SetQuitOnClose(bool quit_on_close);
// Return a RECT representing the bounds of the current client area.
RECT GetClientArea();
protected:
// Processes and route salient window messages for mouse handling,
// size change and DPI. Delegates handling of these to member overloads that
// inheriting classes can handle.
virtual LRESULT MessageHandler(HWND window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;
// Called when CreateAndShow is called, allowing subclass window-related
// setup. Subclasses should return false if setup fails.
virtual bool OnCreate();
// Called when Destroy is called.
virtual void OnDestroy();
private:
friend class WindowClassRegistrar;
// OS callback called by message pump. Handles the WM_NCCREATE message which
// is passed when the non-client area is being created and enables automatic
// non-client DPI scaling so that the non-client area automatically
// responsponds to changes in DPI. All other messages are handled by
// MessageHandler.
static LRESULT CALLBACK WndProc(HWND const window,
UINT const message,
WPARAM const wparam,
LPARAM const lparam) noexcept;
// Retrieves a class instance pointer for |window|
static Win32Window* GetThisFromHandle(HWND const window) noexcept;
bool quit_on_close_ = false;
// window handle for top level window.
HWND window_handle_ = nullptr;
// window handle for hosted content.
HWND child_content_ = nullptr;
};
#endif // RUNNER_WIN32_WINDOW_H_
export 'src/floating_search_bar.dart';
export 'src/floating_search_bar_actions.dart';
export 'src/floating_search_bar_scroll_notifier.dart';
export 'src/floating_search_bar_transition.dart';
export 'src/widgets/circular_button.dart';
export 'src/widgets/search_to_clear.dart';
// ignore_for_file: public_member_api_docs
part of 'floating_search_bar.dart';
typedef OnQueryChangedCallback = void Function(String query);
typedef OnFocusChangedCallback = void Function(bool isFocused);
/// An [AppBar] with implemented search functionality and other
/// utility functions to implement a material behavior.
///
/// This can be considered the base Widget for the full
/// [FloatingSearchBar].
class FloatingSearchAppBar extends ImplicitlyAnimatedWidget {
const FloatingSearchAppBar({
Key? key,
Duration implicitDuration = const Duration(milliseconds: 500),
Curve implicitCurve = Curves.linear,
required this.body,
this.accentColor,
this.color,
this.colorOnScroll,
this.shadowColor,
this.iconColor,
this.padding,
this.insets,
this.height = 56.0,
this.elevation = 0.0,
this.liftOnScrollElevation = 4.0,
this.hintStyle,
this.titleStyle,
this.brightness,
this.bottom,
this.showCursor = true,
this.alwaysOpened = false,
this.clearQueryOnClose = true,
this.automaticallyImplyDrawerHamburger = true,
this.hideKeyboardOnDownScroll = false,
this.automaticallyImplyBackButton = true,
this.progress = 0.0,
this.transitionDuration = const Duration(milliseconds: 500),
this.transitionCurve = Curves.easeOut,
this.debounceDelay = Duration.zero,
this.title,
this.hint = 'Search...',
this.actions,
this.leadingActions,
this.onQueryChanged,
this.onSubmitted,
this.onFocusChanged,
this.controller,
this.textInputAction = TextInputAction.search,
this.textInputType = TextInputType.text,
this.autocorrect = true,
this.contextMenuBuilder,
this.onKeyEvent,
}) : assert(progress == null || (progress is num || progress is bool)),
super(key, implicitDuration, implicitCurve);
/// to show the cursor or not
final bool showCursor;
/// The widget displayed below the [FloatingSearchAppBar]
final Widget? body;
// * --- Style properties --- *
/// The accent color used for example for the
/// progress indicator
final Color? accentColor;
/// The background color of the bar
final Color? color;
/// The color of the bar when a [Scrollable]
/// inside the [body] was scrolled (i.e. is not at the top)
final Color? colorOnScroll;
/// The shadow color for the elevation
final Color? shadowColor;
/// Can be used to override the `IconThemeDatas` color
final Color? iconColor;
/// The padding of the bar
final EdgeInsetsGeometry? padding;
/// The horizontal spacing between [leadingActions], the input
/// field and [actions]
final EdgeInsetsGeometry? insets;
/// The height of the bar
///
/// Defaults to `56.0`
final double? height;
/// The elevation of the bar
final double? elevation;
/// The elevation of the bar when a [Scrollable]
/// inside the [body] was scrolled (i.e. it's not at the top)
final double liftOnScrollElevation;
/// The [TextStyle] for the hint of the input field
final TextStyle? hintStyle;
/// The [TextStyle] for the title of the bar
final TextStyle? titleStyle;
/// The [Brightness] that is used for adjusting the
/// status bar icon brightness.
///
/// By default the brightness is dynamically calculated
/// based on the brightness of the [color] or
/// the [colorOnScroll] respectively.
final Brightness? brightness;
// * --- Utility --- *
final Widget? bottom;
/// Whether the bar should be always in opened state.
///
/// This is useful for example, if you have a page
/// dedicated only for search.
final bool alwaysOpened;
/// {@macro floating_search_bar.clearQueryOnClose}
final bool clearQueryOnClose;
/// {@macro floating_search_bar.automaticallyImplyDrawerHamburger}
final bool automaticallyImplyDrawerHamburger;
/// {@macro floating_search_bar.automaticallyImplyBackButton}
final bool automaticallyImplyBackButton;
/// Hides the keyboard a [Scrollable] inside the [body] was scrolled and
/// shows it again when the user scrolls to the top.
final bool hideKeyboardOnDownScroll;
/// {@macro floating_search_bar.progress}
final dynamic progress;
/// {@macro floating_search_bar.transitionDuration}
final Duration transitionDuration;
/// {@macro floating_search_bar.transitionCurve}
final Curve transitionCurve;
/// {@macro floating_search_bar.debounceDelay}
final Duration debounceDelay;
/// {@macro floating_search_bar.title}
final Widget? title;
/// {@macro floating_search_bar.hint}
final String? hint;
/// {@macro floating_search_bar.actions}
final List<Widget>? actions;
/// {@macro floating_search_bar.leadingActions}
final List<Widget>? leadingActions;
/// {@macro floating_search_bar.onQueryChanged}
final OnQueryChangedCallback? onQueryChanged;
/// {@macro floating_search_bar.onSubmitted}
final OnQueryChangedCallback? onSubmitted;
/// {@macro floating_search_bar.onFocusChanged}
final OnFocusChangedCallback? onFocusChanged;
/// {@macro floating_search_bar.controller}
final FloatingSearchBarController? controller;
/// {@macro floating_search_bar.textInputAction}
final TextInputAction textInputAction;
/// {@macro floating_search_bar.textInputType}
final TextInputType textInputType;
/// {@macro floating_search_bar.autocorrect}
final bool autocorrect;
/// {@macro floating_search_bar. contextMenuBuilder}
final EditableTextContextMenuBuilder? contextMenuBuilder;
final ValueChanged<KeyEvent>? onKeyEvent;
static FloatingSearchAppBarState? of(BuildContext context) {
return context.findAncestorStateOfType<FloatingSearchAppBarState>();
}
@override
FloatingSearchAppBarState createState() => FloatingSearchAppBarState();
}
class FloatingSearchAppBarState extends ImplicitlyAnimatedWidgetState<
FloatingSearchAppBarStyle, FloatingSearchAppBar> {
final ValueNotifier<String> queryNotifer = ValueNotifier<String>('');
final Handler _handler = Handler();
late final AnimationController controller =
AnimationController(vsync: this, duration: transitionDuration)
..value = isAlwaysOpened ? 1.0 : 0.0
..addListener(() => setState(() {}))
..addStatusListener((AnimationStatus status) {
_setInsets();
if (status == AnimationStatus.dismissed) {
if (widget.clearQueryOnClose) {
clear();
}
}
});
late CurvedAnimation transitionAnimation = CurvedAnimation(
parent: controller,
curve: widget.transitionCurve,
);
late final AnimationController scrollController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 200),
)..addListener(() => setState(() {}));
late final CurvedAnimation scrollAnimation = CurvedAnimation(
parent: scrollController,
curve: Curves.easeInOutCubic,
);
late final TextController _input = TextController()
..addListener(() {
if (_input.text != queryNotifer.value) {
queryNotifer.value = _input.text;
_handler.post(
// Do not add a delay when the query is empty.
_input.text.isEmpty ? Duration.zero : widget.debounceDelay,
() => widget.onQueryChanged?.call(_input.text),
);
}
});
bool _wasUnfocusedOnScroll = false;
bool _isAtTop = true;
bool get isAtTop => _isAtTop;
bool get isAppBar => widget.body != null;
bool get isAlwaysOpened => widget.alwaysOpened;
double get _statusBarHeight => MediaQuery.of(context).viewPadding.top;
Duration get transitionDuration => widget.transitionDuration;
FloatingSearchAppBarStyle get style => value;
Color get backgroundColor => Color.lerp(
style.backgroundColor,
style.colorOnScroll,
scrollAnimation.value,
)!;
bool get hasActions => actions.isNotEmpty;
List<Widget> get actions {
final List<Widget> actions =
widget.actions ?? <Widget>[FloatingSearchBarAction.searchToClear()];
final bool showHamburger = widget.automaticallyImplyDrawerHamburger &&
Scaffold.of(context).hasEndDrawer;
return showHamburger
? <Widget>[
...actions,
FloatingSearchBarAction.hamburgerToBack(isLeading: false)
]
: actions;
}
bool get hasleadingActions => leadingActions.isNotEmpty;
List<Widget> get leadingActions {
final List<Widget> actions = widget.leadingActions ?? const <Widget>[];
final bool showHamburger = widget.automaticallyImplyDrawerHamburger &&
Scaffold.of(context).hasDrawer;
Widget? leading;
if (showHamburger) {
leading = FloatingSearchBarAction.hamburgerToBack();
} else if (widget.automaticallyImplyBackButton &&
(Navigator.canPop(context) || widget.body != null)) {
leading = FloatingSearchBarAction.back(
showIfClosed: Navigator.canPop(context),
);
}
return leading != null ? <Widget>[leading, ...actions] : actions;
}
bool _isOpen = false;
bool get isOpen => _isOpen;
set isOpen(bool value) {
if (value) {
() async {
await controller.forward();
focus();
}();
} else {
unfocus();
if (!widget.alwaysOpened) {
controller.reverse();
}
}
if (widget.alwaysOpened) {
_isOpen = true;
return;
}
if (value != isOpen) {
_isOpen = value;
widget.onFocusChanged?.call(isOpen);
}
}
bool get hasFocus => _input.hasFocus;
set hasFocus(bool value) => value ? focus() : unfocus();
String get query => _input.text;
set query(String value) => _input.text = value;
@override
void initState() {
super.initState();
if (isAlwaysOpened) {
_isOpen = true;
postFrame(_input.requestFocus);
}
_assignController();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_setInsets();
}
@override
void didUpdateWidget(FloatingSearchAppBar oldWidget) {
super.didUpdateWidget(oldWidget);
controller.duration = transitionDuration;
if (widget.transitionCurve != oldWidget.transitionCurve) {
transitionAnimation = CurvedAnimation(
parent: controller,
curve: widget.transitionCurve,
);
}
_assignController();
}
void open() => isOpen = true;
void close() => isOpen = false;
void focus() {
_wasUnfocusedOnScroll = false;
_input.requestFocus();
}
void unfocus() {
_wasUnfocusedOnScroll = false;
_input.clearFocus();
}
void clear() => _input.clear();
void _assignController() => widget.controller?._appBarState = this;
late EdgeInsets insets;
void _setInsets() {
bool hasActions(List<Widget> actions) {
final List<Widget> active = List<Widget>.from(actions)
..retainWhere((Widget action) {
if (action is FloatingSearchBarAction) {
return isOpen ? action.showIfOpened : action.showIfClosed;
} else {
return true;
}
});
return active.isNotEmpty;
}
final bool hasleadingActions = hasActions(leadingActions);
final bool hasEndActions = hasActions(actions);
final bool isDefaultPadding = style.padding.horizontal == 24.0;
final double inset = isDefaultPadding ? 4.0 : 0.0;
insets = EdgeInsets.lerp(
style.insets.copyWith(
left: !hasleadingActions ? inset : null,
right: !hasEndActions ? inset : null,
),
style.insets,
transitionAnimation.value,
)!;
}
@override
Widget build(BuildContext context) {
if (isAppBar) {
return _buildAppBar();
} else {
return _buildBar();
}
}
Widget _buildAppBar() {
final double height = style.height + _statusBarHeight;
double prevPixels = 0.0;
final Brightness brightness = widget.brightness ??
(backgroundColor.computeLuminance() > 0.7
? Brightness.light
: Brightness.dark);
return AnnotatedRegion<SystemUiOverlayStyle>(
value: brightness == Brightness.dark
? const SystemUiOverlayStyle(
statusBarBrightness: Brightness.light,
statusBarIconBrightness: Brightness.light)
: const SystemUiOverlayStyle(
statusBarBrightness: Brightness.dark,
statusBarIconBrightness: Brightness.dark),
child: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) {
if (notification.metrics.axis != Axis.vertical) {
return false;
}
final double pixels = notification.metrics.pixels;
if (widget.hideKeyboardOnDownScroll) {
final bool isDown = pixels > prevPixels;
if (isDown && hasFocus) {
unfocus();
_wasUnfocusedOnScroll = true;
} else if (pixels <= 1.0 && _wasUnfocusedOnScroll && !hasFocus) {
focus();
}
}
final bool isAtTop = pixels < 1.0;
if (isAtTop != _isAtTop) {
_isAtTop = isAtTop;
isAtTop ? scrollController.reverse() : scrollController.forward();
}
prevPixels = pixels;
return false;
},
child: Stack(
clipBehavior: Clip.none,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: height),
child: widget.body,
),
_buildBar(),
],
),
),
);
}
Widget _buildBar() {
final double statusBarHeight = isAppBar ? _statusBarHeight : 0.0;
final double elevation = lerpDouble(
style.elevation,
style.liftOnScrollElevation,
scrollAnimation.value,
)!;
final GestureDetector bar = GestureDetector(
onTap: () {
if (isOpen) {
hasFocus = !hasFocus;
_input.moveCursorToEnd();
} else if (!isAppBar) {
isOpen = true;
}
},
child: Material(
color: backgroundColor,
elevation: elevation,
child: Container(
height: style.height + statusBarHeight,
padding: style.padding.add(EdgeInsets.only(top: statusBarHeight)),
child: _buildInputAndActions(),
),
),
);
return isAvailableSwipeBack
? _getBarWidget(bar)
: WillPopScope(
onWillPop: () async {
if (isOpen && !widget.alwaysOpened) {
isOpen = false;
return false;
}
return true;
},
child: _getBarWidget(bar),
);
}
Stack _getBarWidget(GestureDetector bar) {
return Stack(
clipBehavior: Clip.none,
alignment: Alignment.bottomCenter,
children: <Widget>[
bar,
_FloatingSearchProgressBar(
progress: widget.progress,
color: style.accentColor,
),
],
);
}
Widget _buildInputAndActions() {
final IconThemeData iconTheme =
Theme.of(context).iconTheme.copyWith(color: style.iconColor);
return Row(
children: <Widget>[
FloatingSearchActionBar(
animation: transitionAnimation,
actions: leadingActions,
iconTheme: iconTheme,
),
Expanded(
child: Stack(
clipBehavior: Clip.none,
alignment: AlignmentDirectional.centerStart,
children: <Widget>[
_buildInputField(),
buildGradient(isLeft: true),
buildGradient(isLeft: false),
],
),
),
FloatingSearchActionBar(
animation: transitionAnimation,
actions: actions,
iconTheme: iconTheme,
),
],
);
}
Widget buildGradient({required bool isLeft}) {
return Align(
alignment: isLeft ? Alignment.centerLeft : Alignment.centerRight,
child: Transform.rotate(
angle: isLeft ? pi : 0.0,
child: Container(
width: isLeft ? insets.left : insets.right,
height: double.infinity,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: <Color>[
backgroundColor.withOpacity(0.0),
backgroundColor.withOpacity(1.0),
],
),
),
),
),
);
}
Widget _buildInputField() {
final Animation<double> animation =
transitionAnimation.drive(ValleyingTween());
final bool hasQuery = !widget.clearQueryOnClose && query.isNotEmpty;
final bool showTitle =
widget.title != null || (!hasQuery && query.isNotEmpty);
final double opacity = showTitle ? animation.value : 1.0;
final bool showTextInput =
showTitle ? controller.value > 0.5 : controller.value > 0.0;
Widget input;
if (showTextInput) {
input = KeyboardListener(
focusNode: FocusNode(),
onKeyEvent: widget.onKeyEvent,
child: IntrinsicWidth(
child: TextField(
controller: _input,
showCursor: widget.showCursor,
scrollPadding: EdgeInsets.zero,
scrollPhysics: const NeverScrollableScrollPhysics(),
focusNode: _input.node,
autocorrect: widget.autocorrect,
contextMenuBuilder: widget.contextMenuBuilder,
cursorColor: style.accentColor,
style: style.queryStyle,
textInputAction: widget.textInputAction,
keyboardType: widget.textInputType,
onSubmitted: widget.onSubmitted,
decoration: InputDecoration(
isDense: true,
hintText: widget.hint,
hintStyle: style.hintStyle,
contentPadding: EdgeInsets.zero,
border: InputBorder.none,
errorBorder: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
disabledBorder: InputBorder.none,
focusedErrorBorder: InputBorder.none,
),
),
),
);
} else {
if (widget.title != null) {
input = widget.title!;
if (isAppBar) {
input = DefaultTextStyle(
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).appBarTheme.toolbarTextStyle ??
Theme.of(context).textTheme.titleLarge ??
const TextStyle(),
child: input,
);
}
} else {
final ThemeData theme = Theme.of(context);
final TextTheme textTheme = theme.textTheme;
final TextStyle? textStyle = hasQuery
? style.queryStyle ?? textTheme.titleMedium
: style.hintStyle ??
textTheme.titleMedium?.copyWith(color: theme.hintColor);
input = Text(
hasQuery ? query : widget.hint ?? '',
style: textStyle,
maxLines: 1,
);
}
}
return NotificationListener<ScrollNotification>(
onNotification: (_) => true,
child: SingleChildScrollView(
padding: insets,
scrollDirection: Axis.horizontal,
child: Opacity(
opacity: opacity,
child: input,
),
),
);
}
@override
void dispose() {
queryNotifer.dispose();
controller.dispose();
scrollController.dispose();
_handler.cancel();
super.dispose();
}
// * Implicit animation stuff
@override
FloatingSearchAppBarStyle get newValue {
final ThemeData theme = Theme.of(context);
final AppBarTheme appBar = theme.appBarTheme;
final TextDirection direction = Directionality.of(context);
return FloatingSearchAppBarStyle(
height: widget.height ?? kToolbarHeight,
accentColor: widget.accentColor ?? theme.colorScheme.secondary,
backgroundColor: widget.color ?? theme.cardColor,
iconColor: widget.iconColor ?? theme.iconTheme.color ?? Colors.grey,
colorOnScroll: widget.colorOnScroll ?? appBar.backgroundColor,
shadowColor: widget.shadowColor ?? appBar.shadowColor ?? Colors.black54,
elevation: widget.elevation ?? appBar.elevation ?? 0.0,
liftOnScrollElevation: widget.liftOnScrollElevation,
padding: widget.padding?.resolve(direction) ??
EdgeInsetsDirectional.only(
start: hasleadingActions ? 12 : 16,
end: hasActions ? 12 : 16,
).resolve(direction),
insets: widget.insets?.resolve(direction) ??
EdgeInsetsDirectional.only(
start: hasleadingActions ? 16 : 0,
end: hasActions ? 16 : 0,
).resolve(direction),
hintStyle: widget.hintStyle,
queryStyle: widget.titleStyle,
);
}
@override
FloatingSearchAppBarStyle lerp(
FloatingSearchAppBarStyle a,
FloatingSearchAppBarStyle b,
double t,
) =>
a.scaleTo(b, t);
}
class _FloatingSearchProgressBar extends StatefulWidget {
const _FloatingSearchProgressBar({
required this.progress,
required this.color,
});
final dynamic progress;
final Color color;
@override
_FloatingSearchProgressBarState createState() =>
_FloatingSearchProgressBarState();
}
class _FloatingSearchProgressBarState extends State<_FloatingSearchProgressBar>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
dynamic get progress => widget.progress;
bool get showProgressBar => _controller.value > 0.0;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 150),
vsync: this,
)..addListener(() => setState(() {}));
}
@override
void didUpdateWidget(_FloatingSearchProgressBar oldWidget) {
super.didUpdateWidget(oldWidget);
final bool show = progress != null &&
(progress is num || (progress is bool && progress == true));
show ? _controller.forward() : _controller.reverse();
}
@override
Widget build(BuildContext context) {
const double height = 2.75;
final double? progressValue =
progress is num ? (progress as num).toDouble().clamp(0.0, 1.0) : null;
if (showProgressBar) {
return Opacity(
opacity: _controller.value,
child: SizedBox(
height: height,
child: LinearProgressIndicator(
value: progressValue,
semanticsValue: progressValue?.toStringAsFixed(2),
backgroundColor: Colors.transparent,
valueColor: AlwaysStoppedAnimation<Color>(widget.color),
),
),
);
} else {
return const SizedBox(height: height);
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart'
hide ImplicitlyAnimatedWidget, ImplicitlyAnimatedWidgetState;
import 'package:flutter/services.dart';
import '../material_floating_search_bar_2.dart';
import 'floating_search_bar_dismissable.dart';
import 'search_bar_style.dart';
import 'text_controller.dart';
import 'util/util.dart';
import 'widgets/widgets.dart';
part 'floating_search_app_bar.dart';
// ignore_for_file: public_member_api_docs
typedef FloatingSearchBarBuilder = Widget Function(
BuildContext context, Animation<double> transition);
/// An expandable material floating search bar with customizable
/// transitions similar to the ones used extensively
/// by Google in their apps.
class FloatingSearchBar extends ImplicitlyAnimatedWidget {
const FloatingSearchBar({
Key? key,
Duration implicitDuration = const Duration(milliseconds: 600),
Curve implicitCurve = Curves.linear,
this.body,
this.accentColor,
this.backgroundColor,
this.shadowColor = Colors.black87,
this.iconColor,
this.backdropColor,
this.margins,
this.padding,
this.insets,
this.height = 48.0,
this.elevation = 4.0,
this.width,
this.openWidth,
this.axisAlignment = 0.0,
this.openAxisAlignment,
this.border,
this.borderRadius = const BorderRadius.all(Radius.circular(4)),
this.hintStyle,
this.queryStyle,
this.clearQueryOnClose = true,
this.automaticallyImplyDrawerHamburger = true,
this.automaticallyImplyBackButton = true,
this.closeOnBackdropTap = true,
this.progress = false,
this.transitionDuration = const Duration(milliseconds: 500),
this.transitionCurve = Curves.ease,
this.debounceDelay = Duration.zero,
this.title,
this.hint = 'Search...',
this.actions,
this.leadingActions,
this.onQueryChanged,
this.onSubmitted,
this.onFocusChanged,
this.transition,
required this.builder,
this.controller,
this.textInputAction = TextInputAction.search,
this.textInputType = TextInputType.text,
this.autocorrect = true,
this.contextMenuBuilder,
Duration? showAfter,
this.isScrollControlled = false,
this.physics,
this.scrollController,
this.scrollPadding = const EdgeInsets.symmetric(vertical: 16),
this.showCursor = true,
bool initiallyHidden = false,
this.onKeyEvent,
}) : showAfter =
showAfter ?? (initiallyHidden ? const Duration(days: 1) : null),
super(key, implicitDuration, implicitCurve);
/// The widget displayed below the `FloatingSearchBar`.
///
/// This is useful, if the `FloatingSearchBar` should react
/// to scroll events (i.e. hide from view when a [Scrollable]
/// is being scrolled down and show it again when scrolled up).
final Widget? body;
// * --- Style properties --- *
/// The color used for elements such as the progress
/// indicator.
///
/// Defaults to the themes accent color if not specified.
final Color? accentColor;
/// The color of the card.
///
/// If not specified, defaults to `theme.cardColor`.
final Color? backgroundColor;
/// The color of the shadow drawn when `elevation > 0`.
///
/// If not specified, defaults to `Colors.black54`.
final Color? shadowColor;
/// When specified, overrides the themes icon color for
/// this `FloatingSearchBar`, for example to easily adjust
/// the icon color for all [actions] and [leadingActions].
final Color? iconColor;
/// The color that fills the available space when the
/// `FloatingSearchBar` is opened.
///
/// Typically a black-ish color.
///
/// If not specified, defaults to `Colors.black26`.
final Color? backdropColor;
/// The insets from the edges of its parent.
///
/// This can be used to position the `FloatingSearchBar`.
///
/// If not specifed, the `FloatingSearchBar` will try to
/// position itself at the top offsetted by
/// `MediaQuery.of(context).viewPadding.top` to avoid
/// the status bar.
final EdgeInsetsGeometry? margins;
/// The padding of the card.
///
/// Only the horizontal values will be honored.
final EdgeInsetsGeometry? padding;
/// The padding between [leadingActions], the input field and [actions],
/// respectively.
///
/// Only the horizontal values will be honored.
final EdgeInsetsGeometry? insets;
/// The height of the card.
///
/// If not specified, defaults to `48.0` pixels.
final double height;
/// The elevation of the card.
///
/// See also:
/// * [shadowColor] to adjust the color of the shadow.
final double elevation;
/// The max width of the `FloatingSearchBar`.
///
/// By default the `FloatingSearchBar` will expand
/// to fill all the available width. This value can
/// be set to avoid this.
final double? width;
/// The max width of the `FloatingSearchBar` when opened.
///
/// This can be used, when the max width when opened should
/// be different from the one specified by [width].
///
/// When not specified, will use the value of [width].
final double? openWidth;
/// How the `FloatingSearchBar` should be aligned when the
/// available width is bigger than the width specified by [width].
///
/// When not specified, defaults to `0.0` which centers
/// the `FloatingSearchBar`.
final double? axisAlignment;
/// How the `FloatingSearchBar` should be aligned when the
/// available width is bigger than the width specified by [openWidth].
///
/// When not specified, will use the value of [axisAlignment].
final double? openAxisAlignment;
/// The border of the card.
final BorderSide? border;
/// The [BorderRadius] of the card.
///
/// When not specified, defaults to `BorderRadius.circular(4)`.
final BorderRadius? borderRadius;
/// The [TextStyle] for the hint in the [TextField].
final TextStyle? hintStyle;
/// The [TextStyle] for the input of the [TextField].
final TextStyle? queryStyle;
// * --- Utility --- *
/// {@template floating_search_bar.clearQueryOnClose}
/// Whether the current query should be cleared when
/// the `FloatingSearchBar` was closed.
///
/// When not specifed, defaults to `true`.
/// {@endtemplate}
final bool clearQueryOnClose;
/// {@template floating_search_bar.automaticallyImplyDrawerHamburger}
/// Whether a hamburger menu should be shown when
/// there is a [Scaffold] with a [Drawer] in the widget
/// tree.
///
/// When not specified, defaults to `true`.
/// {@endtemplate}
final bool automaticallyImplyDrawerHamburger;
/// {@template floating_search_bar.automaticallyImplyBackButton}
/// Whether to automatically display a back button if the enclosing route
/// can be popped.
///
/// When not specified, defaults to `true`.
/// {@endtemplate}
final bool automaticallyImplyBackButton;
/// Whether the `FloatingSearchBar` should be closed when
/// the backdrop was tapped.
///
/// When not specified, defaults to `true`.
final bool closeOnBackdropTap;
/// {@template floating_search_bar.progress}
/// The progress of the [LinearProgressIndicator] inside the bar.
///
/// When set to a `double` between [0..1], will show
/// show a determined [LinearProgressIndicator].
///
/// When set to `true`, the `FloatingSearchBar` will
/// show an indetermined [LinearProgressIndicator].
///
/// When `null` or `false`, will hide the [LinearProgressIndicator].
/// {@endtemplate}
final dynamic progress;
/// {@template floating_search_bar.transitionDuration}
/// The duration of the animation between opened and closed
/// state.
/// {@endtemplate}
final Duration transitionDuration;
/// {@template floating_search_bar.transitionCurve}
/// The curve for the animation between opened and closed
/// state.
/// {@endtemplate}
final Curve transitionCurve;
/// {@template floating_search_bar.debounceDelay}
/// The delay between the time the user stopped typing
/// and the invocation of the [onQueryChanged] callback.
///
/// This is useful for example if you want to avoid doing
/// expensive tasks, such as making a network call, for every
/// single character.
/// {@endtemplate}
final Duration debounceDelay;
/// {@template floating_search_bar.title}
/// A widget that is shown in place of the [TextField] when the
/// `FloatingSearchBar` is closed.
/// {@endtemplate}
final Widget? title;
/// {@template floating_search_bar.hint}
/// The text value of the hint of the [TextField].
/// {@endtemplate}
final String? hint;
/// {@template floating_search_bar.actions}
/// A list of widgets displayed in a row after the [TextField].
///
/// Consider using [FloatingSearchBarAction]s for more advanced
/// actions that can interact with the `FloatingSearchBar`.
///
/// In LTR languages, they will be displayed to the left of
/// the [TextField].
/// {@endtemplate}
final List<Widget>? actions;
/// {@template floating_search_bar.leadingActions}
/// A list of widgets displayed in a row before the [TextField].
///
/// Consider using [FloatingSearchBarAction]s for more advanced
/// actions that can interact with the `FloatingSearchBar`.
///
/// In LTR languages, they will be displayed to the right of
/// the [TextField].
/// {@endtemplate}
final List<Widget>? leadingActions;
/// {@template floating_search_bar.onQueryChanged}
/// A callback that gets invoked when the input of
/// the query inside the [TextField] changed.
///
/// See also:
/// * [debounceDelay] to delay the invocation of the callback
/// until the user stopped typing.
/// {@endtemplate}
final OnQueryChangedCallback? onQueryChanged;
/// {@template floating_search_bar.onSubmitted}
/// A callback that gets invoked when the user submitted
/// their query (e.g. hit the search button).
/// {@endtemplate}
final OnQueryChangedCallback? onSubmitted;
/// {@template floating_search_bar.onFocusChanged}
/// A callback that gets invoked when the `FloatingSearchBar`
/// receives or looses focus.
/// {@endtemplate}
final OnFocusChangedCallback? onFocusChanged;
/// The transition to be used for animating between closed
/// and opened state.
///
/// See also:
/// * [FloatingSearchBarTransition], which is the base class for all transitions
/// and can be used to create your own custom transition.
/// * [ExpandingFloatingSearchBarTransition], which expands to eventually fill
/// all of its available space, similar to the ones in Gmail or Google Maps.
/// * [CircularFloatingSearchBarTransition], which clips its child in an
/// expanding circle while animating.
/// * [SlideFadeFloatingSearchBarTransition], which fades and translate its
/// child.
final FloatingSearchBarTransition? transition;
/// The builder for the body of this `FloatingSearchBar`.
///
/// Usually, a list of items. Note that unless [isScrollControlled]
/// is set to `true`, the body of a `FloatingSearchBar` must not
/// have an unbounded height meaning that `shrinkWrap` should be set
/// to `true` on all [Scrollable]s.
final FloatingSearchBarBuilder builder;
/// {@template floating_search_bar.controller}
/// The controller for this `FloatingSearchBar` which can be used
/// to programatically open, close, show or hide the `FloatingSearchBar`.
/// {@endtemplate}
final FloatingSearchBarController? controller;
/// {@template floating_search_bar.textInputAction}
/// The [TextInputAction] to be used by the [TextField]
/// of this `FloatingSearchBar`.
/// {@endtemplate}
final TextInputAction textInputAction;
/// {@template floating_search_bar.textInputType}
/// The [TextInputType] of the [TextField]
/// of this `FloatingSearchBar`.
/// {@endtemplate}
final TextInputType textInputType;
/// {@template floating_search_bar.autocorrect}
/// Enable or disable autocorrection of the [TextField] of
/// this `FloatingSearchBar`.
/// {@endtemplate}
final bool autocorrect;
/// {@template floating_search_bar. contextMenuBuilder}
/// The [EditableTextContextMenuBuilder] of the [TextField] of
/// this `FloatingSearchBar`.
/// {@endtemplate}
final EditableTextContextMenuBuilder? contextMenuBuilder;
/// Hides the `FloatingSearchBar` intially for the specified
/// duration and then translates it from the top to its position.
///
/// This can be used as a simple enrance animation.
final Duration? showAfter;
// * --- Scrolling --- *
/// Whether the builder of this `FloatingSearchBar` is using its
/// own [Scrollable].
///
/// This will allow the body of the `FloatingSearchBar` to have an
/// unbounded height.
///
///
/// to dismiss itself when tapped below the height of child inside the
/// [Scrollable], when the child is smaller than the avaialble height.
final bool isScrollControlled;
/// The [ScrollPhysics] of the [SingleChildScrollView] for the body of
/// this `FloatingSearchBar`.
final ScrollPhysics? physics;
/// The [ScrollController] of the [SingleChildScrollView] for the body of
/// this `FloatingSearchBar`.
final ScrollController? scrollController;
/// To show the cursor in the textfield or not
final bool showCursor;
/// Allow processing any keypress into the input text.
final ValueChanged<KeyEvent>? onKeyEvent;
/// The [EdgeInsets] of the [SingleChildScrollView] holding the expandable body of
/// this `FloatingSearchBar`.
final EdgeInsets scrollPadding;
@override
FloatingSearchBarState createState() => FloatingSearchBarState();
static FloatingSearchBarState? of(BuildContext context) {
return context.findAncestorStateOfType<FloatingSearchBarState>();
}
}
class FloatingSearchBarState extends ImplicitlyAnimatedWidgetState<
FloatingSearchBarStyle, FloatingSearchBar> {
final GlobalKey<FloatingSearchAppBarState> barKey = GlobalKey();
FloatingSearchAppBarState? get barState => barKey.currentState;
late final AnimationController _controller = AnimationController(
vsync: this,
duration: duration,
)..addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.dismissed) {
_onClosed();
}
});
late CurvedAnimation animation =
CurvedAnimation(parent: _controller, curve: curve);
late final AnimationController _translateController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 800),
);
late CurvedAnimation _translateAnimation = CurvedAnimation(
parent: _translateController,
curve: Curves.easeInOut,
);
late Widget body;
final ValueNotifier<int> rebuilder = ValueNotifier<int>(0);
late FloatingSearchBarTransition transition =
widget.transition ?? SlideFadeFloatingSearchBarTransition();
late ScrollController _scrollController =
widget.scrollController ?? ScrollController();
dynamic get progress => widget.progress;
FloatingSearchBarStyle get style => value;
Widget? get title => widget.title;
String get hint => widget.hint?.toString() ?? '';
Curve get curve => widget.transitionCurve;
Duration get duration => widget.transitionDuration;
Duration get queryCallbackDelay => widget.debounceDelay;
bool get isOpen => barState?.isOpen ?? false;
set isOpen(bool value) {
if (value != isOpen) {
barState?.isOpen = value;
}
value ? _controller.forward() : _controller.reverse();
}
bool get isVisible => _translateController.isDismissed;
set isVisible(bool value) {
if (value == isVisible) {
return;
}
// Only hide the bar when it is not opened.
if (!isOpen) {
value ? _translateController.reverse() : _translateController.forward();
}
}
void rebuild() => rebuilder.value++;
double _offset = 0.0;
double get offset => _offset;
double get v => animation.value;
bool get isAnimating => _controller.isAnimating;
@override
void initState() {
super.initState();
if (widget.showAfter != null) {
_translateController.value = 1.0;
if (widget.showAfter! < const Duration(days: 1)) {
Future<void>.delayed(widget.showAfter!, show);
}
}
transition = widget.transition ?? SlideFadeFloatingSearchBarTransition();
transition.searchBar = this;
_assignController();
postFrame(rebuild);
}
@override
void didUpdateWidget(FloatingSearchBar oldWidget) {
super.didUpdateWidget(oldWidget);
if (curve != oldWidget.transitionCurve) {
animation = CurvedAnimation(parent: _controller, curve: curve);
}
if (duration != oldWidget.transitionDuration) {
_controller.duration = duration;
}
if (widget.transition != null) {
transition = widget.transition!;
transition.searchBar = this;
}
if (widget.scrollController != null &&
widget.scrollController != _scrollController) {
_scrollController = widget.scrollController!;
}
_assignController();
}
void _assignController() => widget.controller?._searchBarState = this;
void show() => isVisible = true;
void hide() => isVisible = false;
void open() => isOpen = true;
void close() => isOpen = false;
Future<bool> _onPop() async {
if (isOpen) {
close();
return false;
}
return true;
}
void _onClosed() {
_offset = 0.0;
if (!widget.isScrollControlled) {
_scrollController.jumpTo(0.0);
}
}
EdgeInsets _resolve(EdgeInsetsGeometry insets) =>
insets.resolve(Directionality.of(context));
bool _onBuilderScroll(ScrollNotification notification) {
_offset = notification.metrics.pixels;
transition.onBodyScrolled();
return false;
}
double _lastPixel = 0.0;
void _setTranslateCurve(Curve curve) {
_translateAnimation = CurvedAnimation(
parent: _translateController,
curve: curve,
);
}
bool _onBodyScroll(FloatingSearchBarScrollNotification notification) {
if (_controller.isDismissed) {
final double pixel = notification.metrics.pixels;
final bool didReleasePointer = pixel == _lastPixel;
if (didReleasePointer) {
_setTranslateCurve(Curves.easeInOutCubic);
final bool hide = pixel > 0.0 && _translateController.value > 0.5;
hide ? _translateController.forward() : _translateController.reverse();
} else {
_setTranslateCurve(Curves.linear);
final double delta = pixel - _lastPixel;
// ScrollView jumped, do nothing in this case.
if (delta.abs() > 100) {
_lastPixel = pixel;
return false;
}
_translateController.value +=
delta / (style.height + style.margins.top);
_lastPixel = pixel;
}
}
return false;
}
@override
Widget build(BuildContext context) {
transition.searchBar = this;
body = widget.builder(context, animation);
final SizedBox searchBar = SizedBox.expand(
child: isAvailableSwipeBack
? _getSearchBarWidget()
: WillPopScope(
onWillPop: _onPop,
child: _getSearchBarWidget(),
),
);
if (widget.body != null) {
final NotificationListener<FloatingSearchBarScrollNotification> body =
NotificationListener<FloatingSearchBarScrollNotification>(
onNotification: _onBodyScroll,
child: widget.body!,
);
return Stack(
clipBehavior: Clip.none,
fit: StackFit.expand,
children: <Widget>[
body,
RepaintBoundary(
child: searchBar,
),
],
);
} else {
return searchBar;
}
}
NotificationListener<ScrollNotification> _getSearchBarWidget() {
return NotificationListener<ScrollNotification>(
onNotification: _onBuilderScroll,
child: ValueListenableBuilder<int>(
valueListenable: rebuilder,
builder: (BuildContext context, __, _) => AnimatedBuilder(
animation: animation,
builder: (BuildContext context, _) => Stack(
clipBehavior: Clip.none,
children: <Widget>[
_buildBackdrop(),
_buildSearchBar(),
],
),
),
),
);
}
Widget _buildSearchBar() {
final EdgeInsets padding = _resolve(transition.lerpPadding());
final BorderRadius borderRadius = transition.lerpBorderRadius();
final Semantics container = Semantics(
hidden: !isVisible,
focusable: true,
focused: isOpen,
child: Padding(
padding: transition.lerpMargin(),
child: AnimatedBuilder(
animation: CurvedAnimation(
parent: _translateAnimation,
curve: const Interval(0.95, 1.0),
),
builder: (BuildContext context, Widget? child) => Material(
elevation: transition.lerpElevation() *
(1.0 - interval(0.95, 1.0, _translateAnimation.value)),
shadowColor: style.shadowColor,
borderRadius: borderRadius,
child: child,
),
child: Container(
width: transition.lerpWidth(),
height: transition.lerpHeight(),
padding: EdgeInsets.only(top: padding.top, bottom: padding.bottom),
alignment: Alignment.center,
decoration: BoxDecoration(
color: transition.lerpBackgroundColor(),
border: Border.fromBorderSide(style.border),
borderRadius: borderRadius,
),
child: ClipRRect(
borderRadius: borderRadius,
child: _buildInnerBar(),
),
),
),
),
);
final SlideTransition bar = SlideTransition(
position: Tween<Offset>(
begin: Offset.zero,
end: const Offset(0.0, -1.0),
).animate(_translateAnimation),
child: container,
);
return AnimatedAlign(
duration: isAnimating ? duration : Duration.zero,
curve: widget.transitionCurve,
alignment: Alignment(
isOpen ? style.openAxisAlignment : style.axisAlignment, -1.0),
child: transition.isBodyInsideSearchBar
? bar
: Column(
children: <Widget>[
bar,
Expanded(child: _buildBody()),
],
),
);
}
Widget _buildInnerBar() {
final FloatingSearchAppBar textField = FloatingSearchAppBar(
showCursor: widget.showCursor,
body: null,
key: barKey,
height: 1000,
controller: widget.controller,
color: transition.lerpBackgroundColor(),
onFocusChanged: (bool isFocused) {
isOpen = isFocused;
widget.onFocusChanged?.call(isFocused);
},
implicitDuration: widget.duration,
implicitCurve: widget.curve,
title: widget.title,
actions: widget.actions,
leadingActions: widget.leadingActions,
autocorrect: widget.autocorrect,
clearQueryOnClose: widget.clearQueryOnClose,
debounceDelay: widget.debounceDelay,
hint: widget.hint,
onQueryChanged: widget.onQueryChanged,
onSubmitted: widget.onSubmitted,
progress: widget.progress,
automaticallyImplyDrawerHamburger:
widget.automaticallyImplyDrawerHamburger,
automaticallyImplyBackButton: widget.automaticallyImplyBackButton,
contextMenuBuilder: widget.contextMenuBuilder,
transitionDuration: widget.transitionDuration,
transitionCurve: widget.transitionCurve,
textInputAction: widget.textInputAction,
textInputType: widget.textInputType,
accentColor: widget.accentColor,
hintStyle: widget.hintStyle,
iconColor: widget.iconColor,
insets: style.insets,
padding: style.padding,
titleStyle: widget.queryStyle,
shadowColor: style.shadowColor,
onKeyEvent: widget.onKeyEvent,
);
return SizedBox.expand(
child: Stack(
clipBehavior: Clip.none,
alignment: Alignment.topCenter,
children: <Widget>[
if (transition.isBodyInsideSearchBar && v > 0.0)
Positioned.fill(
child: Padding(
padding: EdgeInsets.only(top: style.height),
child: Align(
alignment: Alignment.topCenter,
child: _buildBody(),
),
),
),
Material(
elevation: transition.lerpInnerElevation(),
shadowColor: style.shadowColor,
child: Container(
height: style.height,
color: transition.lerpBackgroundColor(),
alignment: Alignment.topCenter,
child: Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: <Widget>[
SizedBox(
width: transition.lerpInnerWidth(),
child: textField,
),
Align(
alignment: Alignment.bottomCenter,
child: transition.buildDivider(),
),
],
),
),
),
],
),
);
}
Widget _buildBody() {
final Widget body = transition.buildTransition(
widget.isScrollControlled
? this.body
: FloatingSearchBarDismissable(
controller: _scrollController,
padding: widget.scrollPadding,
physics: widget.physics,
child: this.body,
),
);
return IgnorePointer(
ignoring: v < 1.0,
child: SizedBox(
width: (transition.isBodyInsideSearchBar
? transition.lerpInnerWidth()
: transition.lerpWidth()) +
transition.lerpMargin().horizontal,
child: body,
),
);
}
Widget _buildBackdrop() {
if (v == 0.0) {
return const SizedBox(height: 0);
}
return FadeTransition(
opacity: animation,
child: GestureDetector(
onTap: () {
if (widget.closeOnBackdropTap) {
close();
}
},
child: Container(
width: double.infinity,
height: double.infinity,
color: style.backdropColor,
),
),
);
}
@override
void dispose() {
_controller.dispose();
widget.controller?._searchBarState = null;
super.dispose();
}
// * Implicit stuff
@override
FloatingSearchBarStyle get newValue {
final ThemeData theme = Theme.of(context);
final TextDirection direction = Directionality.of(context);
return FloatingSearchBarStyle(
height: widget.height,
elevation: widget.elevation,
maxWidth: widget.width,
openMaxWidth: widget.openWidth,
axisAlignment: widget.axisAlignment ?? 0.0,
openAxisAlignment:
widget.openAxisAlignment ?? widget.axisAlignment ?? 0.0,
backgroundColor: widget.backgroundColor ?? theme.cardColor,
shadowColor: widget.shadowColor ?? Colors.black45,
backdropColor: widget.backdropColor ??
widget.transition?.backdropColor ??
Colors.black26,
border: widget.border ?? BorderSide.none,
borderRadius: widget.borderRadius ?? BorderRadius.circular(4),
margins: (widget.margins ??
EdgeInsets.fromLTRB(
8, MediaQuery.of(context).viewPadding.top + 6, 8, 0))
.resolve(direction),
padding: widget.padding?.resolve(direction) ??
const EdgeInsets.symmetric(horizontal: 12),
insets: widget.insets?.resolve(direction) ??
const EdgeInsets.symmetric(horizontal: 8),
);
}
@override
FloatingSearchBarStyle lerp(
FloatingSearchBarStyle a, FloatingSearchBarStyle b, double t) =>
a.scaleTo(b, t);
}
/// A controller for a [FloatingSearchBar].
class FloatingSearchBarController {
/// Creates a controller for a [FloatingSearchBar].
FloatingSearchBarController();
FloatingSearchAppBarState? _appBarState;
FloatingSearchBarState? _searchBarState;
/// Opens/Expands the [FloatingSearchBar].
void open() => _appBarState?.open();
/// Closes/Collapses the [FloatingSearchBar].
void close() => _appBarState?.close();
/// Visually reveals the [FloatingSearchBar] when
/// it was previously hidden via [hide].
void show() => _searchBarState?.show();
/// Visually hides the [FloatingSearchBar].
void hide() => _searchBarState?.hide();
/// Sets the query of the input of the [FloatingSearchBar].
set query(String query) {
if (_appBarState == null) {
postFrame(() => _appBarState?.query = query);
} else {
_appBarState?.query = query;
}
}
/// The current query of the [FloatingSearchBar].
String get query => _appBarState?.query ?? '';
/// Cleares the current query.
void clear() => _appBarState?.clear();
/// Whether the [FloatingSearchBar] is currently
/// opened/expanded.
bool get isOpen => _appBarState?.isOpen ?? false;
/// Whether the [FloatingSearchBar] is currently
/// closed/collapsed.
bool get isClosed => _appBarState?.isOpen == false;
/// Whether the [FloatingSearchBar] is currently
/// not hidden.
bool get isVisible => _searchBarState?.isVisible ?? false;
/// Whether the [FloatingSearchBar] is currently
/// not visible.
bool get isHidden => _searchBarState?.isVisible == false;
/// Disposes this controller.
void dispose() {
_searchBarState = null;
_appBarState = null;
}
}
import 'package:flutter/material.dart';
import 'floating_search_bar.dart';
import 'widgets/widgets.dart';
// ignore_for_file: public_member_api_docs
/// A widget to be displayed in a row before or after the
/// input text of a [FloatingSearchBar].
///
/// Typically this widget wraps a [CircularButton].
class FloatingSearchBarAction extends StatelessWidget {
/// Creates a widget to be displayed in a row before or after the
/// input text of a [FloatingSearchBar].
///
/// Typically this widget wraps a [CircularButton].
const FloatingSearchBarAction({
super.key,
this.child,
this.builder,
this.showIfOpened = false,
this.showIfClosed = true,
}) : assert(builder != null || child != null);
/// A hamburger menu that when tapped opens the [Drawer]
/// of the nearest [Scaffold].
///
/// When the [FloatingSearchBar] opens, the hamburger
/// transitions into a back button.
factory FloatingSearchBarAction.hamburgerToBack({
double size = 24,
Color? color,
bool isLeading = true,
}) {
return FloatingSearchBarAction(
showIfOpened: true,
builder: (BuildContext context, Animation<double> animation) {
final bool isLTR = Directionality.of(context) == TextDirection.ltr;
return AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget? icon) => CircularButton(
tooltip: animation.isDismissed
? MaterialLocalizations.of(context).openAppDrawerTooltip
: MaterialLocalizations.of(context).backButtonTooltip,
onPressed: () {
final FloatingSearchAppBarState? bar =
FloatingSearchAppBar.of(context);
if (bar?.isOpen ?? false) {
bar?.close();
} else {
Scaffold.of(context).openDrawer();
}
},
icon: icon!,
),
child: RotatedBox(
quarterTurns: (isLTR ? 0 : 2) + (isLeading ? 0 : 2),
child: AnimatedIcon(
icon: AnimatedIcons.menu_arrow,
// Menu arrow has some weird errors on LTR...
// Always use LTR and rotate the widget manually
// for now.
textDirection: TextDirection.ltr,
progress: animation,
color: color,
size: size,
),
),
);
},
);
}
/// A search icon that transitions into a clear icon
/// when the query of the [FloatingSearchBar] is not empty.
factory FloatingSearchBarAction.searchToClear({
double size = 24,
Color? color,
bool showIfClosed = true,
Duration duration = const Duration(milliseconds: 900),
String searchButtonSemanticLabel = 'Search',
String clearButtonSemanticLabel = 'Clear',
}) {
return FloatingSearchBarAction(
showIfOpened: true,
showIfClosed: showIfClosed,
builder: (BuildContext context, Animation<double> animation) {
final FloatingSearchAppBarState bar = FloatingSearchAppBar.of(context)!;
return ValueListenableBuilder<String>(
valueListenable: bar.queryNotifer,
builder: (BuildContext context, String query, _) {
final bool isEmpty = query.isEmpty;
return SearchToClear(
isEmpty: isEmpty,
size: size,
color: color ?? bar.style.iconColor,
duration: duration * 0.5,
onTap: () {
if (!isEmpty) {
bar.clear();
} else {
bar.isOpen =
!bar.isOpen || (!bar.hasFocus && bar.isAlwaysOpened);
}
},
searchButtonSemanticLabel: searchButtonSemanticLabel,
clearButtonSemanticLabel: clearButtonSemanticLabel,
);
},
);
},
);
}
factory FloatingSearchBarAction.back({
double size = 24,
Color? color,
bool showIfClosed = false,
}) {
return FloatingSearchBarAction(
showIfClosed: showIfClosed,
showIfOpened: true,
builder: (BuildContext context, Animation<double> animation) {
final bool canPop = Navigator.canPop(context);
return CircularButton(
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
size: size,
icon: Icon(Icons.arrow_back, color: color, size: size),
onPressed: () {
final FloatingSearchAppBarState bar =
FloatingSearchAppBar.of(context)!;
if (bar.isOpen && !bar.isAlwaysOpened) {
bar.close();
} else if (canPop) {
Navigator.pop(context);
}
},
);
},
);
}
/// A convenience factory to wrap an [Icon] or an [IconData]
/// into an action.
factory FloatingSearchBarAction.icon({
/// Must be a Widget or IconData
required dynamic icon,
required VoidCallback onTap,
double size = 24.0,
bool showIfOpened = false,
bool showIfClosed = true,
}) {
return FloatingSearchBarAction(
showIfClosed: showIfClosed,
showIfOpened: showIfOpened,
child: CircularButton(
size: size,
icon: icon is IconData ? Icon(icon) : icon as Widget,
onPressed: onTap,
),
);
}
/// The action.
///
/// Typically a [CircularButton].
final Widget? child;
/// A builder that can be used when the action needs
/// to react to changes in its [FloatingSearchBar].
///
/// View [FloatingSearchBarAction.searchToClear] for an example.
final Widget Function(BuildContext context, Animation<double> animation)?
builder;
/// Whether this action should be shown when the [FloatingSearchBar]
/// is opened.
///
/// If false, this action will be animated out when the
/// bar [FloatingSearchBar] closed.
final bool showIfOpened;
/// Whether this action should be shown when the [FloatingSearchBar]
/// is closed.
///
/// If false, this action will be animated out when the
/// bar [FloatingSearchBar] closed.
final bool showIfClosed;
/// Whether this [FloatingSearchBarAction] is shown when opened
/// and when closed.
bool get isAlwaysShown => showIfOpened && showIfClosed;
@override
Widget build(BuildContext context) {
return child ??
builder!(
context,
FloatingSearchAppBar.of(context)!.transitionAnimation,
);
}
}
/// Creates a row for [FloatingSearchBarActions].
class FloatingSearchActionBar extends StatelessWidget {
const FloatingSearchActionBar({
super.key,
required this.animation,
required this.actions,
this.iconTheme,
});
final Animation<double> animation;
final List<Widget> actions;
final IconThemeData? iconTheme;
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget? child) => IconTheme(
data: iconTheme ?? Theme.of(context).iconTheme,
child: Row(
children: _mapActions(),
),
),
);
}
List<Widget> _mapActions() {
final Animation<double> animation =
ValleyingTween().animate(this.animation);
final bool isOpen = this.animation.value >= 0.5;
int openCount = 0;
int closedCount = 0;
for (final Widget action in actions) {
if (action is FloatingSearchBarAction) {
if (action.showIfOpened) {
openCount++;
}
if (action.showIfClosed) {
closedCount++;
}
}
}
final List<Widget> currentActions = List<Widget>.from(actions)
..removeWhere((Widget action) {
if (action is FloatingSearchBarAction) {
return (isOpen && !action.showIfOpened) ||
(!isOpen && !action.showIfClosed);
} else {
return false;
}
});
return currentActions.map((Widget action) {
if (action is FloatingSearchBarAction) {
if (action.isAlwaysShown) {
return action;
}
final int index = currentActions.reversed.toList().indexOf(action);
final bool shouldScale =
index <= ((isOpen ? closedCount : openCount) - 1);
if (shouldScale) {
return ScaleTransition(
scale: animation,
child: action,
);
} else {
return SizeFadeTransition(
animation: animation,
axis: Axis.horizontal,
axisAlignment: 1.0,
sizeFraction: 0.25,
child: Center(child: action),
);
}
}
return action;
}).toList();
}
}
import 'dart:math';
import 'package:flutter/material.dart';
import 'floating_search_bar.dart';
import 'util/util.dart';
// ignore_for_file: public_member_api_docs
/// A utility widget that wraps its child in a [SingleChildScrollView]
/// and dismisses the [FloatingSearchBar] when it was tapped below the
/// child.
///
/// This widget is necessary as a [Scrollable] expands to fill its
/// available space and also intercepts all touch events, thus we need
/// to wrap the [Scrollable] inside a [GestureDetector], intercept the tap
/// events before they get to the [Scrollable] and then decide based on the
/// height of the child, whether a tap was below the content.
class FloatingSearchBarDismissable extends StatefulWidget {
const FloatingSearchBarDismissable({
super.key,
required this.child,
this.padding,
this.controller,
this.physics,
});
final Widget child;
/// The amount of space by which to inset the child.
final EdgeInsetsGeometry? padding;
/// An object that can be used to control the position to which this scroll
/// view is scrolled.
///
/// Must be null if [primary] is true.
///
/// A [ScrollController] serves several purposes. It can be used to control
/// the initial scroll position (see [ScrollController.initialScrollOffset]).
/// It can be used to control whether the scroll view should automatically
/// save and restore its scroll position in the [PageStorage] (see
/// [ScrollController.keepScrollOffset]). It can be used to read the current
/// scroll position (see [ScrollController.offset]), or change it (see
/// [ScrollController.animateTo]).
final ScrollController? controller;
/// How the scroll view should respond to user input.
///
/// For example, determines how the scroll view continues to animate after the
/// user stops dragging the scroll view.
///
/// Defaults to matching platform conventions.
final ScrollPhysics? physics;
@override
State<FloatingSearchBarDismissable> createState() =>
_FloatingSearchBarDismissableState<dynamic>();
}
class _FloatingSearchBarDismissableState<E>
extends State<FloatingSearchBarDismissable> {
final GlobalKey<State<StatefulWidget>> childKey = GlobalKey();
double childHeight = 0.0;
double tapDy = 0.0;
double scrollOffset = 0.0;
void _measure() {
postFrame(
() => childHeight = childKey.height ?? 0.0,
);
}
@override
Widget build(BuildContext context) {
_measure();
final EdgeInsets padding =
widget.padding?.resolve(Directionality.of(context)) ?? EdgeInsets.zero;
return GestureDetector(
onTapDown: (TapDownDetails details) => tapDy = details.localPosition.dy,
onPanDown: (DragDownDetails details) => tapDy = details.localPosition.dy,
onPanUpdate: (DragUpdateDetails details) =>
tapDy = details.localPosition.dy,
onTap: () {
final double offset = max(scrollOffset - padding.top, 0.0);
void close() => FloatingSearchBar.of(context)!.close();
if (tapDy < padding.top) {
close();
} else if (tapDy > (childHeight - offset)) {
close();
}
},
child: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) {
final ScrollMetrics metrics = notification.metrics;
if (metrics.axis == Axis.vertical) {
scrollOffset = metrics.pixels;
}
return false;
},
child: SingleChildScrollView(
controller: widget.controller,
physics: widget.physics,
padding: padding.add(
EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
),
child: NotificationListener<SizeChangedLayoutNotification>(
onNotification: (_) {
_measure();
return true;
},
child: SizeChangedLayoutNotifier(
key: childKey,
child: widget.child,
),
),
),
),
);
}
}
import 'package:flutter/material.dart';
/// A Widget that notifies a parent [FloatingSearchBar] about
/// scroll events of its child [Scrollable].
///
/// This is useful, if you want to implement the common pattern
/// with floating search bars, in which the search bar is hidden
/// when the user scrolls down and shown again when the user scrolls
/// up.
class FloatingSearchBarScrollNotifier extends StatelessWidget {
/// Creates a [FloatingSearchBarScrollNotifier].
///
/// This widget is useful, if you want to implement the common pattern
/// with floating search bars, in which the search bar is hidden
/// when the user scrolls down and shown again when the user scrolls
/// up.
const FloatingSearchBarScrollNotifier({
super.key,
required this.child,
});
/// The vertically scrollable child.
final Widget child;
@override
Widget build(BuildContext context) {
return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) {
ScrollMetrics metrics = notification.metrics;
// Dispatch the notifcation only for vertical
// scrollables.
if (metrics.axis == Axis.vertical) {
// If the scroll notification is due to an "over-scroll" (stretch, or animating the release of a stretch)
// then force the scroll-metric to it's zero position (position the search bar at it's pinned position).
// This is to prevent the search bar from animating away, then back again when the stretch animation
// finishes.
if (metrics.pixels < 0 || metrics.pixels > metrics.maxScrollExtent) {
metrics = FixedScrollMetrics(
pixels: metrics.pixels < 0 ? 0 : metrics.maxScrollExtent,
axisDirection: metrics.axisDirection,
maxScrollExtent: metrics.maxScrollExtent,
minScrollExtent: metrics.minScrollExtent,
viewportDimension: metrics.viewportDimension,
devicePixelRatio: metrics.devicePixelRatio,
);
}
FloatingSearchBarScrollNotification(
metrics,
context,
).dispatch(context);
}
return false;
},
child: child,
);
}
}
/// The [ScrollNotifcation] used by [FloatingSearchBarScrollNotifier].
class FloatingSearchBarScrollNotification extends ScrollNotification {
/// Creates a [ScrollNotifcation] used by [FloatingSearchBarScrollNotifier].
FloatingSearchBarScrollNotification(
ScrollMetrics metrics,
BuildContext context,
) : super(
metrics: metrics,
context: context,
);
}
// ignore_for_file: avoid_equals_and_hash_code_on_mutable_classes
import 'dart:math' as math;
import 'dart:ui';
import 'package:flutter/material.dart';
import 'floating_search_bar.dart';
import 'search_bar_style.dart';
import 'util/util.dart';
import 'widgets/widgets.dart';
/// Base class for all open/close transitions
/// for a [FloatingSearchBar].
///
/// This class exposes various fields from the [FloatingSearchBar]
/// and lets it interpolate them based on the animation progress.
///
/// See also:
/// * [ExpandingFloatingSearchBarTransition], which expands to eventually fill
/// all of its available space, similar to the ones in Gmail or Google Maps.
/// * [CircularFloatingSearchBarTransition], which clips its child in an
/// expanding circle while animating.
/// * [SlideFadeFloatingSearchBarTransition], which fades and translate its
/// child.
abstract class FloatingSearchBarTransition {
late FloatingSearchBarState searchBar;
BuildContext get context => searchBar.context;
CurvedAnimation get animation => searchBar.animation;
double get t => searchBar.v;
FloatingSearchBarStyle get style => searchBar.style;
double get offset => searchBar.offset;
double get fullHeight => context.height ?? 0.0;
double get fullWidth => context.width ?? 0.0;
double get height => style.height;
double get elevation => style.elevation;
EdgeInsets get padding => style.padding.resolve(Directionality.of(context));
EdgeInsets get margin => style.margins.resolve(Directionality.of(context));
Color get backgroundColor => style.backgroundColor;
BorderRadius get borderRadius => style.borderRadius;
double? get maxWidth => style.maxWidth;
double? get openMaxWidth => style.openMaxWidth;
bool get isBodyInsideSearchBar;
Color get backdropColor => Colors.black38;
double lerpHeight() => height;
double lerpElevation() => elevation;
double lerpInnerElevation() => 0.0;
double lerpWidth() {
if (maxWidth == null && openMaxWidth != null) {
return lerpDouble(fullWidth, openMaxWidth, t)!;
} else {
return lerpDouble(
maxWidth ?? fullWidth,
openMaxWidth ?? maxWidth ?? fullWidth,
t,
)!;
}
}
double lerpInnerWidth() => lerpWidth();
EdgeInsetsGeometry lerpPadding() => padding;
EdgeInsetsGeometry lerpMargin() => margin;
Color lerpBackgroundColor() => backgroundColor;
BorderRadius lerpBorderRadius() => borderRadius;
Widget buildTransition(Widget content) => content;
Widget buildDivider() => const SizedBox(height: 0);
void onBodyScrolled() {}
void rebuild() => searchBar.rebuild();
@override
// ignore: hash_and_equals
bool operator ==(dynamic other) => other.runtimeType == runtimeType;
}
/// A [FloatingSearchBarTransition]
/// {@template expanding_floating_search_bar_transition}
/// which expands to eventually fill all of its available space,
/// similar to the ones in Gmail or Google Maps.
///
/// An example of this can be viewed [here](https://github.com/bxqm/material_floating_search_bar/blob/master/assets/expanding_example.gif):
/// {@endtemplate}
class ExpandingFloatingSearchBarTransition extends FloatingSearchBarTransition {
/// Creates a [FloatingSearchBarTransition]
/// {@macro expanding_floating_search_bar_transition}
ExpandingFloatingSearchBarTransition({
this.innerElevation = 8,
this.divider,
});
/// The elevation of the bar to create a lift on scroll effect
/// when the body of the [FloatingSearchBar] gets scrolled beneath the
/// bar.
final double innerElevation;
/// A divider to be shown between the bar and the body of the [FloatingSearchBar].
final Widget? divider;
@override
bool get isBodyInsideSearchBar => true;
@override
Color get backdropColor => Colors.transparent;
@override
double lerpHeight() => lerpDouble(height, fullHeight, t)!;
@override
double lerpWidth() => lerpDouble(maxWidth ?? fullWidth, fullWidth, t)!;
@override
double lerpInnerWidth() {
return lerpDouble(
maxWidth ?? fullWidth,
openMaxWidth ?? fullWidth,
t,
)!;
}
@override
double lerpInnerElevation() {
return lerpDouble(
0.0,
innerElevation,
(offset / (innerElevation * 10)).clamp(0.0, 1.0),
)!;
}
@override
EdgeInsetsGeometry lerpPadding() {
return EdgeInsetsGeometry.lerp(
padding,
padding.copyWith(top: margin.top),
t,
)!;
}
@override
EdgeInsetsGeometry lerpMargin() =>
EdgeInsetsGeometry.lerp(margin, EdgeInsets.zero, t)!;
@override
BorderRadius lerpBorderRadius() =>
BorderRadius.lerp(borderRadius, BorderRadius.zero, t)!;
@override
void onBodyScrolled() {
if (lerpInnerElevation() < innerElevation) {
rebuild();
}
}
@override
Widget buildDivider() {
return Opacity(
opacity: t,
child: divider ??
const Opacity(
opacity: 0.75,
child: Divider(
height: 0,
thickness: 1,
),
),
);
}
}
/// The base class for all overlaying [FloatingSearchBarTransition]s, which are
/// those, where the body of the [FloatingSearchBar] is displayed outside of the
/// bar.
abstract class OverlayingFloatingSearchBarTransition
extends FloatingSearchBarTransition {
OverlayingFloatingSearchBarTransition({
double? spacing,
this.divider,
}) : _spacing = spacing;
/// The vertical spacing between the bar of the [FloatingSearchBar] and its body.
final double? _spacing;
/// A divider to seperate the body of the [FloatingSearchBar] from the bar.
///
/// Typically this gets revealed when the body has scrolled the amount specifieds
/// by [spacing].
final Widget? divider;
double get spacing => _spacing ?? searchBar.widget.scrollPadding.top;
@override
bool get isBodyInsideSearchBar => false;
bool get reachedTop => spacing <= offset;
double get scrollT {
if (spacing == 0.0) {
return offset <= 0.0 ? 0.0 : 1.0;
} else {
return (offset / spacing).clamp(0.0, 1.0) * t;
}
}
@override
Widget buildDivider() {
return Opacity(
opacity: scrollT,
child: divider ??
Container(
height: 2 * scrollT,
color: Theme.of(context).dividerColor,
),
);
}
@override
BorderRadius lerpBorderRadius() {
if (spacing == 0.0) {
return super.lerpBorderRadius();
}
return BorderRadius.lerp(
borderRadius,
BorderRadius.only(
topLeft: borderRadius.topLeft,
topRight: borderRadius.topRight,
),
scrollT,
)!;
}
@override
Widget buildTransition(Widget content) {
final EdgeInsets margin =
this.margin.resolve(Directionality.of(context)).copyWith(
top: 0.0,
bottom: 0.0,
);
return Padding(
padding: margin,
child: content,
);
}
@override
void onBodyScrolled() {
if (offset < spacing) {
rebuild();
}
}
@override
bool operator ==(dynamic other) {
if (identical(this, other)) {
return true;
}
return other is OverlayingFloatingSearchBarTransition &&
other.spacing == spacing &&
other.divider == divider;
}
@override
int get hashCode => spacing.hashCode ^ divider.hashCode;
}
/// A [FloatingSearchBarTransition]
/// {@template circular_floating_search_bar_transition}
/// which clips its child in an expanding circle.
///
/// An example of this can be viewed [here](https://github.com/bxqm/material_floating_search_bar/blob/master/assets/circular_example.gif):
/// {@endtemplate}
class CircularFloatingSearchBarTransition
extends OverlayingFloatingSearchBarTransition {
/// Creates a [FloatingSearchBarTransition],
/// {@macro circular_floating_search_bar_transition}
CircularFloatingSearchBarTransition({
super.spacing,
super.divider,
});
@override
Widget buildTransition(Widget content) {
final double spacing = math.max(this.spacing - offset, 0.0);
return super.buildTransition(
Transform.translate(
offset: Offset(0, -spacing * (1 - t)),
child: CircularReveal(
fraction: t,
origin: Alignment.bottomCenter,
child: content,
),
),
);
}
}
/// A [FloatingSearchBarTransition]
/// {@template fade_in_floating_search_bar_transition}
/// which fades and vertically translates its child.
///
/// An example of this can be viewed [here](https://github.com/bxqm/material_floating_search_bar/blob/master/assets/slide_fade_example.gif)
/// {@endtemplate}
class SlideFadeFloatingSearchBarTransition
extends OverlayingFloatingSearchBarTransition {
/// Creates a [FloatingSearchBarTransition],
/// {@macro fade_in_floating_search_bar_transition}
SlideFadeFloatingSearchBarTransition({
super.spacing,
super.divider,
this.translation = 32.0,
});
final double translation;
@override
Widget buildTransition(Widget content) {
final double offset = lerpDouble(
translation,
0.0,
Curves.easeIn.transform(t),
)!;
return super.buildTransition(
Transform.translate(
offset: Offset(0, offset),
child: Opacity(
opacity: t,
child: content,
),
),
);
}
}
// ignore_for_file: avoid_equals_and_hash_code_on_mutable_classes
import 'dart:ui';
import 'package:flutter/material.dart';
class FloatingSearchBarStyle {
const FloatingSearchBarStyle({
required this.backgroundColor,
required this.shadowColor,
required this.padding,
required this.insets,
required this.height,
required this.elevation,
required this.backdropColor,
required this.border,
required this.borderRadius,
required this.margins,
required this.maxWidth,
required this.openMaxWidth,
required this.axisAlignment,
required this.openAxisAlignment,
});
final Color backgroundColor;
final Color shadowColor;
final Color backdropColor;
final EdgeInsets padding;
final EdgeInsets insets;
final EdgeInsets margins;
final double height;
final double elevation;
final BorderSide border;
final BorderRadius borderRadius;
final double? maxWidth;
final double? openMaxWidth;
final double axisAlignment;
final double openAxisAlignment;
FloatingSearchBarStyle scaleTo(FloatingSearchBarStyle b, double t) {
return FloatingSearchBarStyle(
height: lerpDouble(height, b.height, t)!,
elevation: lerpDouble(elevation, b.elevation, t)!,
maxWidth: b.maxWidth == null ? null : lerpDouble(maxWidth, b.maxWidth, t),
openMaxWidth: b.openMaxWidth == null
? null
: lerpDouble(openMaxWidth, b.openMaxWidth, t),
axisAlignment: lerpDouble(axisAlignment, b.axisAlignment, t)!,
openAxisAlignment: lerpDouble(openAxisAlignment, b.openAxisAlignment, t)!,
backgroundColor: Color.lerp(backgroundColor, b.backgroundColor, t)!,
backdropColor: Color.lerp(backdropColor, b.backdropColor, t)!,
shadowColor: Color.lerp(shadowColor, b.shadowColor, t)!,
insets: EdgeInsets.lerp(insets, b.insets, t)!,
margins: EdgeInsets.lerp(margins, b.margins, t)!,
padding: EdgeInsets.lerp(padding, b.padding, t)!,
border: BorderSide.lerp(border, b.border, t),
borderRadius: BorderRadius.lerp(borderRadius, b.borderRadius, t)!,
);
}
@override
String toString() {
return 'FloatingSearchBarStyle(backgroundColor: $backgroundColor, shadowColor: $shadowColor, padding: $padding, insets: $insets, height: $height, elevation: $elevation, backdropColor: $backdropColor, border: $border, borderRadius: $borderRadius, margins: $margins, maxWidth: $maxWidth, openMaxWidth: $openMaxWidth, axisAlignment: $axisAlignment, openAxisAlignment: $openAxisAlignment)';
}
}
class FloatingSearchAppBarStyle {
const FloatingSearchAppBarStyle({
required this.accentColor,
required this.backgroundColor,
required this.shadowColor,
required this.iconColor,
required this.colorOnScroll,
required this.padding,
required this.insets,
required this.height,
required this.elevation,
required this.liftOnScrollElevation,
required this.hintStyle,
required this.queryStyle,
});
final Color accentColor;
final Color backgroundColor;
final Color shadowColor;
final Color iconColor;
final Color? colorOnScroll;
final EdgeInsets padding;
final EdgeInsets insets;
final double height;
final double elevation;
final double liftOnScrollElevation;
final TextStyle? hintStyle;
final TextStyle? queryStyle;
FloatingSearchAppBarStyle scaleTo(FloatingSearchAppBarStyle b, double t) {
return FloatingSearchAppBarStyle(
height: lerpDouble(height, b.height, t)!,
elevation: lerpDouble(elevation, b.elevation, t)!,
liftOnScrollElevation:
lerpDouble(liftOnScrollElevation, b.liftOnScrollElevation, t)!,
accentColor: Color.lerp(accentColor, b.accentColor, t)!,
backgroundColor: Color.lerp(backgroundColor, b.backgroundColor, t)!,
colorOnScroll: Color.lerp(colorOnScroll, b.colorOnScroll, t),
shadowColor: Color.lerp(shadowColor, b.shadowColor, t)!,
iconColor: Color.lerp(iconColor, b.iconColor, t)!,
insets: EdgeInsets.lerp(insets, b.insets, t)!,
padding: EdgeInsets.lerp(padding, b.padding, t)!,
hintStyle: TextStyle.lerp(hintStyle, b.hintStyle, t),
queryStyle: TextStyle.lerp(queryStyle, b.queryStyle, t),
);
}
@override
String toString() {
return 'FloatingSearchAppBarStyle(accentColor: $accentColor, backgroundColor: $backgroundColor, shadowColor: $shadowColor, iconColor: $iconColor, colorOnScroll: $colorOnScroll, padding: $padding, insets: $insets, height: $height, elevation: $elevation, liftOnScrollElevation: $liftOnScrollElevation, hintStyle: $hintStyle, queryStyle: $queryStyle)';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
return other is FloatingSearchAppBarStyle &&
other.accentColor == accentColor &&
other.backgroundColor == backgroundColor &&
other.shadowColor == shadowColor &&
other.iconColor == iconColor &&
other.colorOnScroll == colorOnScroll &&
other.padding == padding &&
other.insets == insets &&
other.height == height &&
other.elevation == elevation &&
other.liftOnScrollElevation == liftOnScrollElevation &&
other.hintStyle == hintStyle &&
other.queryStyle == queryStyle;
}
@override
int get hashCode {
return accentColor.hashCode ^
backgroundColor.hashCode ^
shadowColor.hashCode ^
iconColor.hashCode ^
colorOnScroll.hashCode ^
padding.hashCode ^
insets.hashCode ^
height.hashCode ^
elevation.hashCode ^
liftOnScrollElevation.hashCode ^
hintStyle.hashCode ^
queryStyle.hashCode;
}
}
import 'package:flutter/material.dart';
/// A [TextEditingController] that wraps a [FocusNode]
/// and maintains the selection extent when the text
/// changes.
class TextController extends TextEditingController {
/// Creates a [TextEditingController] that wraps a [FocusNode]
/// and maintains the selection extent when the text
/// changes.
TextController();
/// The [FocusNode] of this [TextController].
final FocusNode node = FocusNode();
@override
set text(String newText) {
int offset = selection.extentOffset;
// When the current selection is at the end of the
// query, adjust the selection to the new end of the
// query.
final bool isSelectionAtTextEnd = text.length == offset;
if (isSelectionAtTextEnd) {
offset = newText.length;
}
value = value.copyWith(
text: newText,
selection: TextSelection.collapsed(offset: offset),
composing: TextRange.empty,
);
}
/// Moves the Cursor to the end of the current text.
void moveCursorToEnd() =>
selection = TextSelection.collapsed(offset: text.length);
/// Request focus for the [FocusNode] wrapped by this
/// [TextController].
void requestFocus() {
node.requestFocus();
}
/// Cleares the focus of the [FocusNode] wrapped by this
/// [TextController].
void clearFocus() {
node.unfocus(
disposition: UnfocusDisposition.previouslyFocusedChild,
);
}
/// Whether the [FocusNode] wrapped by this
/// [TextController] is currenty focused.
bool get hasFocus => node.hasFocus;
@override
void dispose() {
node.dispose();
super.dispose();
}
}
import 'dart:math' as math;
import 'package:flutter/material.dart';
// ignore_for_file: public_member_api_docs
extension NumExtension on num {
double get radians => this * (math.pi / 180.0);
double get degrees => this * (180.0 / math.pi);
}
extension GlobalKeyExtension on GlobalKey {
RenderBox? get renderBox => currentContext?.renderBox;
Size? get size => renderBox?.hasSize ?? false ? renderBox?.size : Size.zero;
double? get height => size?.height;
double? get width => size?.width;
}
extension BuildContextUiExtension on BuildContext {
RenderBox? get renderBox => findRenderObject() as RenderBox?;
Size? get size => renderBox?.hasSize ?? false ? renderBox?.size : Size.zero;
double? get height => size?.height;
double? get width => size?.width;
}
import 'dart:async';
import 'package:flutter/rendering.dart';
// ignore_for_file: public_member_api_docs
/// A class that can schedule a function to run at a later time using Future.delayed.
/// Additionally it supports canceling the future by not invoking the callback function
/// if it was canceled before.
class Handler {
Timer? _timer;
void call(dynamic delay, VoidCallback callback) => post(delay, callback);
void post(dynamic delay, VoidCallback callback) {
assert(delay is num || delay is Duration);
Duration duration;
if (delay is num) {
duration = Duration(milliseconds: delay.toInt());
} else {
duration = delay as Duration;
}
_timer?.cancel();
_timer = Timer(duration, callback);
}
void cancel() => _timer?.cancel();
bool get isCanceled => _timer?.isActive == false;
}
Handler post(dynamic delay, VoidCallback callback) {
return Handler()..post(delay, callback);
}
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
export 'extensions.dart';
export 'handler.dart';
// ignore_for_file: public_member_api_docs
double interval(double begin, double end, double t,
{Curve curve = Curves.linear}) {
if (t < begin) {
return 0.0;
}
if (t > end) {
return 1.0;
}
final double v = ((t - begin) / (end - begin)).clamp(0.0, 1.0);
return curve.transform(v);
}
void postFrame(VoidCallback callback) {
WidgetsBinding.instance!.addPostFrameCallback((_) => callback());
}
bool get isAvailableSwipeBack {
bool macLand = false;
if (!kIsWeb) {
macLand = Platform.isIOS | Platform.isMacOS;
}
return macLand;
}
import 'package:flutter/material.dart';
import 'implicit_animation_builder.dart';
// ignore_for_file: public_member_api_docs
class AnimatedTranslation extends StatelessWidget {
const AnimatedTranslation({
Key? key,
required this.translation,
required this.duration,
required this.child,
this.curve = Curves.linear,
this.isFractional = false,
}) : super(key: key);
final Offset translation;
final Duration duration;
final Widget child;
final Curve curve;
final bool isFractional;
@override
Widget build(BuildContext context) {
return ImplicitAnimationBuilder<Offset>(
lerp: (Offset a, Offset b, double t) => Offset.lerp(a, b, t)!,
value: translation,
curve: curve,
duration: duration,
builder: (BuildContext context, Offset value, _) {
if (isFractional) {
return FractionalTranslation(
translation: value,
child: child,
);
} else {
return Transform.translate(
offset: value,
child: child,
);
}
},
);
}
}
import 'dart:ui';
import 'package:flutter/material.dart';
import 'implicit_animation_builder.dart';
// ignore_for_file: public_member_api_docs
class AnimatedValue extends StatelessWidget {
const AnimatedValue({
super.key,
required this.value,
required this.duration,
this.curve = Curves.linear,
required this.builder,
});
final double value;
final Duration duration;
final Curve curve;
final Widget Function(BuildContext context, double value) builder;
@override
Widget build(BuildContext context) {
return ImplicitAnimationBuilder<double>(
value: value,
curve: curve,
duration: duration,
lerp: (double a, double b, double t) => lerpDouble(a, b, t)!,
builder: (BuildContext context, double value, _) =>
builder(context, value),
);
}
}
import 'package:flutter/animation.dart';
export 'animated_translation.dart';
export 'animated_value.dart';
export 'circular_reveal_transition.dart';
export 'implicit_animation_builder.dart';
export 'implicitly_animated_widget.dart';
export 'size_fade_transition.dart';
/// A [TweenSequence] that goes from 0 to 1 and back to 0.
class PeakingTween extends TweenSequence<double> {
/// Creates a [TweenSequence] that goes from 0 to 1 and back to 0.
PeakingTween()
: super(<TweenSequenceItem<double>>[
TweenSequenceItem<double>(
tween: Tween<double>(begin: 0.0, end: 1.0), weight: 0.5),
TweenSequenceItem<double>(
tween: Tween<double>(begin: 1.0, end: 0.0), weight: 0.5),
]);
}
/// A [TweenSequence] that goes from 1 to 0 and back to 1.
class ValleyingTween extends TweenSequence<double> {
/// Creates a [TweenSequence] that goes from 1 to 0 and back to 1.
ValleyingTween()
: super(<TweenSequenceItem<double>>[
TweenSequenceItem<double>(
tween: Tween<double>(begin: 1.0, end: 0.0), weight: 0.5),
TweenSequenceItem<double>(
tween: Tween<double>(begin: 0.0, end: 1.0), weight: 0.5),
]);
}
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
// ignore_for_file: public_member_api_docs
class CircularReveal extends StatelessWidget {
const CircularReveal({
super.key,
this.fraction = 1.0,
this.origin = Alignment.center,
this.offset = Offset.zero,
this.child,
this.minRadius,
this.maxRadius,
});
final double fraction;
final Alignment origin;
final Offset offset;
final Widget? child;
final double? minRadius;
final double? maxRadius;
@override
Widget build(BuildContext context) {
return ClipPath(
clipper: _CircularRevealClipper(
origin: origin,
offset: offset,
fraction: fraction,
minRadius: minRadius,
maxRadius: maxRadius,
),
child: child,
);
}
}
class _CircularRevealClipper extends CustomClipper<Path> {
const _CircularRevealClipper({
this.fraction = 1.0,
this.origin = Alignment.center,
this.offset = Offset.zero,
this.minRadius,
this.maxRadius,
});
final double fraction;
final Alignment origin;
final Offset offset;
final double? minRadius;
final double? maxRadius;
@override
Path getClip(Size size) {
final double x = (origin.x + 1.0) / 2.0;
final double y = 1.0 - ((origin.y + 1.0) / 2.0);
final Offset center =
Offset((size.width * x) + offset.dx, (size.height * y) + offset.dy);
final double minRadius = this.minRadius ?? 0.0;
final double maxRadius = this.maxRadius ?? calcMaxRadius(size, center);
final double radius = lerpDouble(minRadius, maxRadius, fraction) ?? 0;
return Path()
..addOval(
Rect.fromCircle(
center: center,
radius: radius,
),
);
}
double calcMaxRadius(Size size, Offset center) {
final double w = max(center.dx, size.width - center.dx);
final double h = max(center.dy, size.height - center.dy);
return sqrt((w * w) + (h * h));
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) {
if (oldClipper is _CircularRevealClipper) {
return oldClipper.fraction != fraction ||
oldClipper.origin != origin ||
oldClipper.offset != offset;
}
return false;
}
}
import 'package:flutter/material.dart'
hide ImplicitlyAnimatedWidget, ImplicitlyAnimatedWidgetState;
import 'implicitly_animated_widget.dart';
// ignore_for_file: public_member_api_docs
class ImplicitAnimationBuilder<T> extends ImplicitlyAnimatedWidget {
const ImplicitAnimationBuilder({
Key? key,
required Duration duration,
Curve curve = Curves.linear,
required this.lerp,
required this.value,
required this.builder,
this.child,
}) : super(key, duration, curve);
final T Function(T a, T b, double t) lerp;
final T value;
final Widget Function(BuildContext context, T value, Widget? child) builder;
final Widget? child;
@override
State<ImplicitAnimationBuilder<T>> createState() =>
_ImplicitAnimationBuilderState<T>();
}
class _ImplicitAnimationBuilderState<T>
extends ImplicitlyAnimatedWidgetState<T, ImplicitAnimationBuilder<T>> {
@override
T get newValue => widget.value;
@override
T lerp(T a, T b, double t) => widget.lerp(a, b, t);
@override
Widget build(BuildContext context) =>
widget.builder(context, value, widget.child);
}
import 'package:flutter/material.dart'
hide ImplicitlyAnimatedWidget, ImplicitlyAnimatedWidgetState;
// ignore_for_file: public_member_api_docs
/// A base Widget for implicit animations.
abstract class ImplicitlyAnimatedWidget extends StatefulWidget {
const ImplicitlyAnimatedWidget(
Key? key,
this.duration,
this.curve,
) : super(key: key);
final Duration duration;
final Curve curve;
}
abstract class ImplicitlyAnimatedWidgetState<T,
W extends ImplicitlyAnimatedWidget> extends State<W>
with TickerProviderStateMixin {
late final AnimationController _controller;
late final CurvedAnimation _animation;
T get newValue;
late T value = newValue;
late T oldValue = newValue;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: widget.duration,
vsync: this,
)
..value = 1.0
..addListener(() => setState(
() => value = lerp(oldValue, newValue, _animation.value),
));
_animation = CurvedAnimation(
curve: widget.curve,
parent: _controller,
);
}
@override
void didUpdateWidget(W oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.duration != widget.duration) {
_controller.duration = widget.duration;
}
if (oldWidget.curve != widget.curve) {
_animation = CurvedAnimation(
curve: widget.curve,
parent: _controller,
);
}
if (value != newValue) {
oldValue = value;
_controller
..reset()
..forward();
}
}
T lerp(T a, T b, double t);
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
import 'package:flutter/material.dart';
// ignore_for_file: public_member_api_docs
class SizeFadeTransition extends StatefulWidget {
const SizeFadeTransition({
super.key,
required this.animation,
this.sizeFraction = 0.5,
this.curve = Curves.linear,
this.axis = Axis.vertical,
this.axisAlignment = 0.0,
this.child,
}) : assert(sizeFraction >= 0.0 && sizeFraction <= 1.0);
final Animation<double> animation;
final Curve curve;
final double sizeFraction;
final Axis axis;
final double axisAlignment;
final Widget? child;
@override
State<SizeFadeTransition> createState() => _SizeFadeTransitionState();
}
class _SizeFadeTransitionState extends State<SizeFadeTransition> {
late final CurvedAnimation curve =
CurvedAnimation(parent: widget.animation, curve: widget.curve);
late final CurvedAnimation size =
CurvedAnimation(curve: Interval(0.0, widget.sizeFraction), parent: curve);
late final CurvedAnimation opacity =
CurvedAnimation(curve: Interval(widget.sizeFraction, 1.0), parent: curve);
@override
Widget build(BuildContext context) {
return SizeTransition(
sizeFactor: size,
axis: widget.axis,
axisAlignment: widget.axisAlignment,
child: FadeTransition(
opacity: opacity,
child: widget.child,
),
);
}
}
import 'package:flutter/material.dart';
/// An easier to use [IconButton] for [FloatingSearchBar]
/// actions.
class CircularButton extends StatelessWidget {
/// Creates an easier to use [IconButton] for
/// [FloatingSearchBar] actions.
const CircularButton({
super.key,
required this.icon,
required this.onPressed,
this.size = 24.0,
this.padding = const EdgeInsets.all(8),
this.tooltip,
});
// ignore: public_member_api_docs
final Widget icon;
// ignore: public_member_api_docs
final VoidCallback onPressed;
/// The size of this icon.
///
/// When not specified, defaults to `24.0`.
final double size;
/// The padding of this icon.
///
/// When not specified, defaults to
/// `EdgeInsets.all(8)`.
final EdgeInsets padding;
// ignore: public_member_api_docs
final String? tooltip;
@override
Widget build(BuildContext context) {
Widget button = Material(
type: MaterialType.transparency,
child: InkWell(
customBorder: const CircleBorder(),
onTap: onPressed,
child: Padding(
padding: padding,
child: icon,
),
),
);
if (tooltip != null) {
button = Tooltip(
message: tooltip ?? '',
child: button,
);
}
return button;
}
}
import 'package:flutter/material.dart';
import '../util/util.dart';
import 'widgets.dart';
// ignore_for_file: public_member_api_docs
/// A Widget that animates between a search and
/// a clear icon.
class SearchToClear extends StatelessWidget {
/// Creates a Widget that animates between a search and
/// a clear icon.
const SearchToClear({
super.key,
required this.isEmpty,
required this.onTap,
this.duration = const Duration(milliseconds: 500),
this.color,
this.size = 24.0,
this.searchButtonSemanticLabel = 'Search',
this.clearButtonSemanticLabel = 'Clear',
});
/// If `true`, the search icon will be shown.
final bool isEmpty;
final Duration duration;
final VoidCallback onTap;
final Color? color;
final double size;
final String searchButtonSemanticLabel;
final String clearButtonSemanticLabel;
@override
Widget build(BuildContext context) {
return AnimatedValue(
value: isEmpty ? 0.0 : 1.0,
duration: duration,
builder: (BuildContext context, double value) {
return CircularButton(
onPressed: onTap,
tooltip: value == 0.0
? searchButtonSemanticLabel
: clearButtonSemanticLabel,
icon: CustomPaint(
size: Size.square(size),
painter: _SearchToClearPainter(
color ?? Theme.of(context).iconTheme.color ?? Colors.black,
value,
),
),
);
},
);
}
}
class _SearchToClearPainter extends CustomPainter {
_SearchToClearPainter(
this.color,
this.progress,
);
final Color color;
final double progress;
@override
void paint(Canvas canvas, Size size) {
final double w = size.width;
final double h = size.height;
final double t = progress;
final double circleProgress = interval(0.0, 0.4, t, curve: Curves.easeIn);
final double lineProgress = interval(0.3, 0.8, t, curve: Curves.ease);
final double sLineProgress = interval(0.5, 1.0, t, curve: Curves.easeOut);
canvas.clipRect(Rect.fromLTWH(0, 0, w, h));
const double padding = 0.225;
canvas.translate(w * (padding / 2), h * (padding / 2));
canvas.scale(1 - padding, 1 - padding);
final double sw = w * 0.125;
final Paint paint = Paint()
..color = color
..isAntiAlias = true
..strokeWidth = sw
..style = PaintingStyle.stroke;
final double radius = w * 0.26;
final double offset = radius + (sw / 2);
// Draws the handle of the loop.
final Offset lineStart = Offset(radius * 2, radius * 2);
final Offset lineEnd = Offset(sw, sw);
canvas.drawLine(
Offset.lerp(lineStart, lineEnd, lineProgress)!,
Offset(w - sw, h - sw),
paint,
);
// Draws the circle of the loop.
final Offset circleStart = Offset(offset, offset);
final Offset circleEnd = Offset(-offset, -offset);
final Path circle = Path()
..addArc(
Rect.fromCircle(
center: Offset.lerp(circleStart, circleEnd, lineProgress)!,
radius: radius,
),
32.0.radians,
(360 * (1 - circleProgress)).radians,
);
canvas.drawPath(circle, paint);
// Draws the second line that will make the cross.
final Offset sLineStart = Offset(sw, h - sw);
final Offset sLineEnd = Offset(w - sw, sw);
canvas.drawLine(
sLineStart,
Offset.lerp(sLineStart, sLineEnd, sLineProgress)!,
paint,
);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
export 'animation/animation.dart';
export 'circular_button.dart';
export 'search_to_clear.dart';
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.idea" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10"
meta:
dependency: "direct main"
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.2"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
sdks:
dart: ">=2.12.0 <3.0.0"
name: material_floating_search_bar_2
description: A Flutter implementation of an expandable and animated floating search bar, also known as persistent search.
version: 0.5.0
repository: https://github.com/AlwinFassbender/material_floating_search_bar
issue_tracker: https://github.com/AlwinFassbender/material_floating_search_bar/issues
environment:
sdk: ">=2.12.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
meta: ^1.7.0
dev_dependencies:
flutter_test:
sdk: flutter
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment