├── .github
├── FUNDING.yml
└── ISSUE_TEMPLATE
│ └── bug_report.md
├── .gitignore
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README-zh.md
├── README.md
├── analysis_options.yaml
├── ci
├── ci-analysis_options.yaml
├── deploy
├── doc
├── app-release-arm64.apk
├── appbar-badge.gif
├── appbar-corner-fixed.png
├── appbar-corner.png
├── appbar-demo.gif
├── appbar-fixed-circle.gif
├── appbar-fixed.gif
├── appbar-flip.gif
├── appbar-gradient.gif
├── appbar-image.gif
├── appbar-react-circle.gif
├── appbar-react.gif
├── appbar-single-button.png
├── appbar-single-shape.png
├── appbar-textIn.gif
├── appbar-theming.png
├── appbar-titled.gif
├── badge-demo-preview.gif
├── badge-demo.mp4
├── donate-kofi1.png
├── donate-wechat.jpeg
├── flutter-favorite.png
├── how-to-block-tab-event.md
├── issue-change-active-tab-index.md
├── issue-crash-on-flutter-dev-channel.md
├── issue-image-for-actionitem.md
├── issue-remove-elevation.md
├── preview.png
├── tab-hook.gif
└── tabcontroller_pageview.gif
├── example
├── .gitignore
├── .metadata
├── README.md
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── convexappbar
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── res
│ │ │ │ ├── drawable-v21
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── values-night
│ │ │ │ └── styles.xml
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle
├── images
│ ├── sample-1-2.png
│ ├── sample-1.png
│ ├── sample-2-2.png
│ ├── sample-2.png
│ ├── sample-3-2.png
│ ├── sample-3.png
│ ├── sample-4-2.png
│ ├── sample-4.png
│ ├── sample-5-2.png
│ └── sample-5.png
├── ios
│ ├── .gitignore
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ └── WorkspaceSettings.xcsettings
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── Runner
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ │ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── Runner-Bridging-Header.h
├── lib
│ ├── color_item_view.dart
│ ├── components
│ │ ├── chip_item.dart
│ │ ├── choose_tab_item.dart
│ │ ├── colors_item.dart
│ │ ├── gradient_item.dart
│ │ ├── heading.dart
│ │ └── radio_item.dart
│ ├── convex_button_demo.dart
│ ├── custom_appbar_sample.dart
│ ├── data.dart
│ ├── default_appbar_demo.dart
│ ├── main.dart
│ └── model
│ │ ├── badge.dart
│ │ ├── choice_value.dart
│ │ └── named_color.dart
├── pubspec.yaml
└── web
│ ├── favicon.png
│ ├── icons
│ ├── Icon-192.png
│ └── Icon-512.png
│ ├── index.html
│ └── manifest.json
├── lib
├── convex_bottom_bar.dart
└── src
│ ├── bar.dart
│ ├── chip_builder.dart
│ ├── convex_shape.dart
│ ├── fab.dart
│ ├── interface.dart
│ ├── item.dart
│ ├── painter.dart
│ ├── reused_gradient.dart
│ ├── stack.dart
│ └── style
│ ├── blend_image_icon.dart
│ ├── fixed_circle_tab_style.dart
│ ├── fixed_tab_style.dart
│ ├── flip_tab_style.dart
│ ├── inner_builder.dart
│ ├── internal_style_config.dart
│ ├── react_circle_tab_style.dart
│ ├── react_tab_style.dart
│ ├── styles.dart
│ ├── textin_tab_style.dart
│ ├── titled_tab_style.dart
│ ├── transition_container.dart
│ └── transition_container_builder.dart
├── pubspec.yaml
└── test
├── provider_test.dart
├── utils_test.dart
└── widget_test.dart
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: hacktons
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## Notice for Stack compile error
11 | ```
12 | * Replace all Stack.overflow to Stack.clipBehavior.
13 | * The removal of [Stack.overflow][https://api.flutter.dev/flutter/widgets/Stack/overflow.html] is not a good idea, and the Flutter team has noticed that it would take time to remove all the usage without breaking Google.
14 | Since the overflow has been removed in 1.20 and be rolled back now(perhaps 1.22? not for sure). It's hard to say when it will be removed again, so just replace all overflow with clipBehavior.
15 | ```
16 | ---
17 |
18 | > Please search the [issue list](https://github.com/hacktons/convex_bottom_bar/issues) and [FAQ list](https://github.com/hacktons/convex_bottom_bar#faq) before opening any issues!!
19 |
20 | **Describe the bug**
21 |
22 | A clear and concise description of what the bug is.
23 |
24 | **Environment details**
25 |
26 | Paste the flutter environment detail.
27 | ```
28 | flutter doctor
29 | flutter --version
30 | ```
31 | Paste the package version.
32 | ```
33 | dependencies:
34 | convex_bottom_bar: x.y.z
35 | ```
36 |
37 | **To Reproduce**
38 |
39 | Steps to reproduce the behavior:
40 | 1. Go to '...'
41 | 2. Click on '....'
42 | 3. Scroll down to '....'
43 | 4. See error
44 |
45 | **Expected behavior**
46 |
47 | A clear and concise description of what you expected to happen.
48 |
49 | **Screenshots**
50 |
51 | If applicable, add screenshots to help explain your problem.
52 |
53 | **Additional context**
54 |
55 | Add any other context about the problem here.
56 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .packages
28 | .pub-cache/
29 | .pub/
30 | build/
31 | pubspec.lock
32 |
33 | # Android related
34 | **/android/**/gradle-wrapper.jar
35 | **/android/.gradle
36 | **/android/captures/
37 | **/android/gradlew
38 | **/android/gradlew.bat
39 | **/android/local.properties
40 | **/android/**/GeneratedPluginRegistrant.java
41 |
42 | # iOS/XCode related
43 | **/ios/**/*.mode1v3
44 | **/ios/**/*.mode2v3
45 | **/ios/**/*.moved-aside
46 | **/ios/**/*.pbxuser
47 | **/ios/**/*.perspectivev3
48 | **/ios/**/*sync/
49 | **/ios/**/.sconsign.dblite
50 | **/ios/**/.tags*
51 | **/ios/**/.vagrant/
52 | **/ios/**/DerivedData/
53 | **/ios/**/Icon?
54 | **/ios/**/Pods/
55 | **/ios/**/.symlinks/
56 | **/ios/**/profile
57 | **/ios/**/xcuserdata
58 | **/ios/.generated/
59 | **/ios/Flutter/App.framework
60 | **/ios/Flutter/Flutter.framework
61 | **/ios/Flutter/Generated.xcconfig
62 | **/ios/Flutter/app.flx
63 | **/ios/Flutter/app.zip
64 | **/ios/Flutter/flutter_assets/
65 | **/ios/Flutter/flutter_export_environment.sh
66 | **/ios/ServiceDefinitions.json
67 | **/ios/Runner/GeneratedPluginRegistrant.*
68 | **/ios/Podfile.lock
69 |
70 | # Exceptions to above rules.
71 | !**/ios/**/default.mode1v3
72 | !**/ios/**/default.mode2v3
73 | !**/ios/**/default.pbxuser
74 | !**/ios/**/default.perspectivev3
75 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
76 | # Pana report
77 | pana_visual/
78 | coverage/
79 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: cc949a8e8b9cf394b9290a8e80f87af3e207dce5
8 | channel: stable
9 |
10 | project_type: package
11 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [3.2.0]
2 | * Support flutter 3.7
3 |
4 | ## [3.1.0+1]
5 | * Format project with `flutter format .`
6 |
7 | ## [3.1.0]
8 | * Bug fix;
9 | * New API to set shadow color
10 | * Fix lint warning
11 |
12 | ## [3.0.0-nullsafety.1]
13 | * Prepare for Flutter 2.
14 |
15 | ## [2.7.1+2]
16 | * format
17 |
18 | ## [2.7.1+1]
19 | * Bug fix [Manual update tab index workaround seems not working since version 2.7.x](https://github.com/hacktons/convex_bottom_bar/issues/134)
20 |
21 | ## [2.7.1]
22 | * fix runtime exception when there is no controller passed in
23 |
24 | ## [2.7.0+1]
25 | * add new configuration `disableDefaultTabController`
26 |
27 | ## [3.0.0-nullsafety.0]
28 |
29 | * Migrate to [null-safety](https://dart.dev/null-safety/migration-guide).
30 |
31 | ## [2.6.0]
32 | * Fix tab highlight state to satisfy keyboard and router case; [#115](https://github.com/hacktons/convex_bottom_bar/issues/115), [#112](https://github.com/hacktons/convex_bottom_bar/issues/112)
33 | * Breaking changes: disable initial index property when working with TabController to avoid potential index conflict;
34 |
35 | ## [2.5.1+1]
36 | * Bug fix [#111 Tab selection not registered during tab transition](https://github.com/hacktons/convex_bottom_bar/issues/111)
37 |
38 | ## [2.5.1]
39 |
40 | * Replace all Stack.overflow to Stack.clipBehavior.
41 | * The removal of [Stack.overflow][https://api.flutter.dev/flutter/widgets/Stack/overflow.html] is not a good idea, and the Flutter team has noticed that it would take time to remove all the usage without breaking Google.
42 | Since the overflow has been removed in 1.20 and be rolled back now(perhaps 1.22? not for sure). It's hard to say when it will be removed again, so just replace all overflow with clipBehavior.
43 |
44 | ## [2.5.0]
45 | * Support hook api for tab event. [How to block tab event?](https://github.com/hacktons/convex_bottom_bar/blob/master/doc/how-to-block-tab-event.md)
46 |
47 | ## [2.4.2]
48 | * Bug fix [#102 dispose is executed more than once](https://github.com/hacktons/convex_bottom_bar/issues/102)
49 | * Set min version of flutter to 1.12.0
50 |
51 | ## [2.4.1]
52 | * Fix badge alignment issues [#77](https://github.com/hacktons/convex_bottom_bar/issues/77).
53 |
54 | ## [2.4.0]
55 | * Support flutter v1.20, the v2.2.4-flutter-1.20 is now sync with v2.4.0.
56 | * Add new feature. We can now add corner on AppBars's background.
57 |
58 | ## [2.3.0]
59 | * Add convex button widget.
60 |
61 | ## [2.2.5+1]
62 |
63 | * fix format issue.
64 | ```
65 | lib/src/bar.dart is not formatted according to dartfmt
66 | To format your files run: dartfmt -w .
67 | ```
68 |
69 | ## [2.2.5]
70 |
71 | * Bug fix [#67](https://github.com/hacktons/convex_bottom_bar/issues/67)
72 |
73 | ## [2.2.4-flutter-1.20]
74 | * support the flutter v1.20
75 | * fix: overflow property of stack deprecated [#60](https://github.com/hacktons/convex_bottom_bar/pull/60)
76 |
77 | ## [2.2.4]
78 | * Polish internal style, avoid reanimate when AppBar rebuild.
79 |
80 | ## [2.2.3]
81 |
82 | * Bug fix [#59](https://github.com/hacktons/convex_bottom_bar/issues/59)
83 | * Bug fix: default index not match with DefaultTabController.
84 |
85 | ## [2.2.2]
86 | * Fix activate index when appbar is not working with controller;
87 | * Dispose AnimationController when is dirty;
88 |
89 | ## [2.2.1]
90 | * Support RTL;
91 |
92 | ## [2.2.0]
93 |
94 | * Add new API to hook internal styles. Checkout `StyleProvider` for details;
95 | * Provide configuration to hide Text widget when label is empty;
96 |
97 | ## [2.1.1]
98 |
99 | * Fix ChipBuilder missing issue;
100 | * Polish ci test;
101 |
102 | ## [2.1.0+1]
103 |
104 | * Document all the public APIs;
105 |
106 | ## [2.1.0]
107 |
108 | * Add controller for Appbar to change tab index programmaticlly;
109 | * Support with framework's DefaultTabController and TabController;
110 | * Enable access to the ConvexAppBarState;
111 | * Fix active tab position when using even menus;
112 |
113 | ## [2.0.3]
114 |
115 | * Fix hitTest for active tab;
116 |
117 | ## [2.0.2]
118 |
119 | * Add new config parameter for initial active index;
120 |
121 | ## [2.0.1]
122 |
123 | * Update usage instructions in README.md;
124 | * Add new test cases to improve the code coverage;
125 | * Bug fix;
126 |
127 | ## [2.0.0]
128 |
129 | * Support badge on tab item;
130 | * Constructor update, rename the builder;
131 | * Bug fix;
132 |
133 | ## [1.4.1]
134 |
135 | * Add titled style;
136 | * Remove some redundant widget layers;
137 |
138 | ## [1.4.0]
139 |
140 | * Add flip style;
141 | * Add textIn style;
142 |
143 | ## [1.3.1+1]
144 |
145 | * Bug fix: activate icon is not working in some style;
146 | * Improvement: enable image with/without color blend;
147 |
148 | ## [1.3.1]
149 |
150 | * Support gradient background;
151 | * Tab item are generic type, both `IconData` and `Widget` can be used;
152 |
153 | ## [1.3.0]
154 |
155 | * Add new tab style;
156 | * Support animated tab transition;
157 |
158 | ## [1.2.0]
159 |
160 | * Support iPhoneX' safe area at bottom edge. [#issues/7](https://github.com/hacktons/convex_bottom_bar/issues/7)
161 |
162 | ## [1.1.0]
163 |
164 | * Add elevation attribute.
165 |
166 | ## [1.0.2+1]
167 |
168 | * Documentation fixes.
169 |
170 | ## [1.0.2]
171 |
172 | * Fix maintenance suggestions to get higher score
173 |
174 | ## [0.0.1]
175 |
176 | * Publish the packages to pub.dev
177 |
178 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/README-zh.md:
--------------------------------------------------------------------------------
1 |

2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | English
13 | | 简体中文
14 |
15 |
16 | ---
17 |
18 | ConvexBottomBar是一个底部导航栏组件,用于展现凸起的TAB效果,支持多种内置样式与动画交互。你可以在[https://appbar.codemagic.app](https://appbar.codemagic.app)上找到在线样例。
19 |
20 | **convex_bottom_bar 现在是一个 [Flutter Favorite](https://flutter.dev/docs/development/packages-and-plugins/favorites) 插件库!**
21 |
22 |
23 |
24 |
25 |
26 | 以下是一些支持的预定义样式:
27 |
28 | | **fixed** | **react** | **badge chip** |
29 | |:---------------------------------:|:--------------------------------:|:-------------------------:|
30 | |  |  |  |
31 | | **fixedCircle** | **reactCircle** | **flip** |
32 | |  |  |  |
33 | | **textIn** | **titled** | **tab image** |
34 | |  |  |  |
35 | | **button** | **fixed corner** | |
36 | |  |  | |
37 |
38 | ## 快速上手
39 |
40 | 通常ConvexAppBar可以通过设置bottomNavigationBar与Scaffold一起使用。
41 |
42 | ConvexAppBar具有两个构造函数,ConvexAppBar()将使用默认样式来简化选项卡的创建。
43 |
44 | 将此添加到您程序包的pubspec.yaml文件中,注意使用最新版本[](https://pub.dartlang.org/packages/convex_bottom_bar):
45 |
46 | ```yaml
47 | dependencies:
48 | convex_bottom_bar: ^latest_version
49 | ```
50 |
51 | ```dart
52 | import 'package:convex_bottom_bar/convex_bottom_bar.dart';
53 |
54 | Scaffold(
55 | bottomNavigationBar: ConvexAppBar(
56 | items: [
57 | TabItem(icon: Icons.home, title: 'Home'),
58 | TabItem(icon: Icons.map, title: 'Discovery'),
59 | TabItem(icon: Icons.add, title: 'Add'),
60 | TabItem(icon: Icons.message, title: 'Message'),
61 | TabItem(icon: Icons.people, title: 'Profile'),
62 | ],
63 | onTap: (int i) => print('click index=$i'),
64 | )
65 | );
66 | ```
67 |
68 | **Flutter Version Support**
69 | 由于Flutter迭代非常快。SDK本身有可能出现不兼容的API变更,我们将继续支持flutter稳定版本,非稳定的beta、dev channel
70 | 通过单独版本号进行兼容。
71 |
72 | | **Stable Flutter Version** | **Package Version** | **More** |
73 | |:--------------------------:|:-------------------:|:------------------------------------------:|
74 | | >=3.7.0 | >=3.2.0 | 从v3.7.0版本DefaultTabController的API有变更 |
75 | | >=1.20 | >=2.4.0 | 从 v1.20开始, Stack组件的API发送不兼容变更 |
76 | | <1.20 | <2.4.0 | v1.20稳定版发布后,我们对老版本如v1.17, v1.12 的支持将不再继续更新 |
77 |
78 | 如果你只需要一个单独的按钮,不妨试试 `ConvexButton`.
79 |
80 | ## 功能
81 |
82 | * 提供多种内部样式
83 | * 能够更改AppBar的主题
84 | * 提供Builder API以自定义新样式
85 | * 在AppBar上添加徽章
86 | * 支持优雅的过渡动画
87 | * 提供Hook API来重载一些内部样式
88 | * RTL布局支持
89 |
90 | ## Table of contents
91 |
92 | - [主题](#主题)
93 | - [角标](#角标)
94 | - [单独按钮](#单独按钮)
95 | - [样式重载](#样式重载)
96 | - [RTL支持](#RTL支持)
97 | - [自定义样例](#自定义样例)
98 | - [常见问题](#常见问题)
99 | - [支持](#支持)
100 |
101 | ## 角标
102 | 如果需要在TAB上添加徽章/角标,请使用`ConvexAppBar.badge`来构建。
103 |
104 | [](doc/badge-demo.mp4 "badge demo")
105 |
106 | ```dart
107 | ConvexAppBar.badge({0: '99+', 1: Icons.assistant_photo, 2: Colors.redAccent},
108 | items: [
109 | TabItem(icon: Icons.home, title: 'Home'),
110 | TabItem(icon: Icons.map, title: 'Discovery'),
111 | TabItem(icon: Icons.add, title: 'Add'),
112 | ],
113 | onTap: (int i) => print('click index=$i'),
114 | );
115 | ```
116 |
117 | `badge()`方法接受一个角标数组; 角标是带有选项卡项的映射,每个条目的值可以是String,IconData,Color或Widget。
118 |
119 | ## 单独按钮
120 | 
121 |
122 | ```dart
123 | Scaffold(
124 | appBar: AppBar(title: const Text('ConvexButton Example')),
125 | body: Center(child: Text('count $count')),
126 | bottomNavigationBar: ConvexButton.fab(
127 | onTap: () => setState(() => count++),
128 | ),
129 | );
130 | ```
131 |
132 | ## 主题
133 | AppBar默认使用内置样式,您可能需要为其设置主题。 以下是一些支持的属性:
134 |
135 | 
136 |
137 | | Attributes | Description |
138 | |-----------------|--------------------------------------------------------|
139 | | backgroundColor | AppBar 背景 |
140 | | gradient | 渐变属性,可以覆盖backgroundColor |
141 | | height | AppBar 高度 |
142 | | color | icon/text 的颜色值 |
143 | | activeColor | icon/text 的**选中态**颜色值 |
144 | | curveSize | 凸形大小 |
145 | | top | 凸形到AppBar上边缘的距离 |
146 | | style | 支持的样式: **fixed, fixedCircle, react, reactCircle**, ... |
147 | | chipBuilder | 角标构造器builder, **ConvexAppBar.badge**会使用默认样式 |
148 |
149 | ## 样式重载
150 | 重载Tab内置样式。 该API与`ConvexAppBar.builder`不同,为了满足您可能需要更新选项卡样式而不定义新的选项卡样式。
151 | **温馨提示:**
152 | 则此Hook能力是有限的,如果您提供的尺寸与内部样式不匹配,并且可能导致`overflow broken`。
153 |
154 | ```dart
155 | StyleProvider(
156 | style: Style(),
157 | child: ConvexAppBar(
158 | initialActiveIndex: 1,
159 | height: 50,
160 | top: -30,
161 | curveSize: 100,
162 | style: TabStyle.fixedCircle,
163 | items: [
164 | TabItem(icon: Icons.link),
165 | TabItem(icon: Icons.import_contacts),
166 | TabItem(title: "2020", icon: Icons.work),
167 | ],
168 | backgroundColor: _tabBackgroundColor,
169 | ),
170 | )
171 | class Style extends StyleHook {
172 | @override
173 | double get activeIconSize => 40;
174 |
175 | @override
176 | double get activeIconMargin => 10;
177 |
178 | @override
179 | double get iconSize => 20;
180 |
181 | @override
182 | TextStyle textStyle(Color color) {
183 | return TextStyle(fontSize: 20, color: color);
184 | }
185 | }
186 | ```
187 |
188 | ## RTL支持
189 | RTL的内部适配,如果你配置了Directionality,将自动支持ltf和rtl两种布局风格。
190 | ```dart
191 | Directionality(
192 | textDirection: TextDirection.rtl,
193 | child: Scaffold(body:ConvexAppBar(/*TODO ...*/)),
194 | )
195 | ```
196 |
197 | ## 自定义样例
198 |
199 | 如果默认样式与您的情况不符,请尝试使用`ConvexAppBar.builder()`,它可以让您自定义几乎所有TAB样式。
200 | ```dart
201 | Scaffold(
202 | bottomNavigationBar: ConvexAppBar.builder(
203 | count: 5,
204 | backgroundColor: Colors.blue,
205 | itemBuilder: Builder(),
206 | )
207 | );
208 |
209 | /*user defined class*/
210 | class Builder extends DelegateBuilder {
211 | @override
212 | Widget build(BuildContext context, int index, bool active) {
213 | return Text('TAB $index');
214 | }
215 | }
216 | ```
217 |
218 | 完整的自定义示例可以在[example](example)中找到。
219 |
220 | ## 常见问题
221 | 如您在使用过程中有新功能建议或者遇到问题,请移步至[issue tracker](https://github.com/hacktons/convex_bottom_bar/issues)提交。
222 |
223 | * [如何拦截Tab事件](doc/how-to-block-tab-event.md)
224 | * [Flutter dev/beta channel运行崩溃](doc/issue-crash-on-flutter-dev-channel.md)
225 | * [动态修改选中的TAB](doc/issue-change-active-tab-index.md)
226 | * [如何给TAB添加图片而不是ICON](doc/issue-image-for-actionitem.md)
227 | * [如何移除AppBar的边缘阴影](doc/issue-remove-elevation.md)
228 |
229 | ## 支持
230 | 如果对你有帮助,微信扫码请作者喝杯咖啡 :)
231 |
232 | 
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | English
13 | | 简体中文
14 |
15 |
16 | ---
17 |
18 | The official BottomAppBar can only display a notch FAB with an app bar, and sometimes we need a convex FAB. BottomAppBar and NotchShape's implementation inspires this ConvexAppBar.
19 |
20 | Online example can be found at [https://appbar.codemagic.app](https://appbar.codemagic.app).
21 |
22 | **convex_bottom_bar is now a [Flutter Favorite](https://flutter.dev/docs/development/packages-and-plugins/favorites) package!**
23 |
24 |
25 |
26 |
27 |
28 | Here are some supported style:
29 |
30 | | **fixed** | **react** | **badge chip** |
31 | |:---------------------------------:|:--------------------------------:|:-------------------------:|
32 | |  |  |  |
33 | | **fixedCircle** | **reactCircle** | **flip** |
34 | |  |  |  |
35 | | **textIn** | **titled** | **tab image** |
36 | |  |  |  |
37 | | **button** | **fixed corner** | |
38 | |  |  | |
39 |
40 | ## How to use
41 | Typically ConvexAppBar can work with `Scaffold` by setup its `bottomNavigationBar`.
42 |
43 | The `ConvexAppBar` has two constructors. The `ConvexAppBar()` will use the default style to simplify the tab creation.
44 |
45 | Add this to your package's pubspec.yaml file, use the latest version [](https://pub.dartlang.org/packages/convex_bottom_bar):
46 |
47 | ```yaml
48 | dependencies:
49 | convex_bottom_bar: ^latest_version
50 | ```
51 |
52 | ```dart
53 | import 'package:convex_bottom_bar/convex_bottom_bar.dart';
54 |
55 | Scaffold(
56 | bottomNavigationBar: ConvexAppBar(
57 | items: [
58 | TabItem(icon: Icons.home, title: 'Home'),
59 | TabItem(icon: Icons.map, title: 'Discovery'),
60 | TabItem(icon: Icons.add, title: 'Add'),
61 | TabItem(icon: Icons.message, title: 'Message'),
62 | TabItem(icon: Icons.people, title: 'Profile'),
63 | ],
64 | onTap: (int i) => print('click index=$i'),
65 | )
66 | );
67 | ```
68 |
69 | **Flutter Version Support**
70 | As Flutter is developing fast. There can be breaking changes. We will be trying to support the
71 | stable version and beta version through different package versions.
72 |
73 | | **Stable Flutter Version** | **Package Version** | **More** |
74 | |:--------------------------:|:-------------------:|:--------------------------------------------------------------------------:|
75 | | >=3.7.0 | >=3.2.0 | Since v3.7.0, the stable version changed the DefaultTabController api |
76 | | >=1.20 | >=2.4.0 | Since v1.20, the stable version changed the Stack api |
77 | | <1.20 | <=2.3.0 | Support for stable version such as v1.17, v1.12 is not going to be updated |
78 |
79 | ## Features
80 | * Provide multiple internal styles
81 | * Ability to change the theme of AppBar
82 | * Provide builder API to customize a new style
83 | * Add badge on the tab menu
84 | * Elegant transition animation
85 | * Provide hook API to override some of the internal styles
86 | * RTL support
87 |
88 | ## Table of contents
89 |
90 | - [Theming](#theming)
91 | - [Badge](#badge)
92 | - [Single Button](#single-button)
93 | - [Style Hook](#style-hook)
94 | - [RTL Support](#rtl-support)
95 | - [Custom Example](#custom-example)
96 | - [FAQ](#faq)
97 | - [Donate](#donate)
98 |
99 | ## Theming
100 | The bar will use default style, you may want to theme it. Here are some supported attributes:
101 |
102 | 
103 |
104 | | Attributes | Description |
105 | |-----------------|--------------------------------------------------------------------------------------|
106 | | backgroundColor | AppBar background |
107 | | gradient | gradient will override backgroundColor |
108 | | height | AppBar height |
109 | | color | tab icon/text color |
110 | | activeColor | tab icon/text color **when selected** |
111 | | curveSize | size of the convex shape |
112 | | top | top edge of the convex shape relative to AppBar |
113 | | cornerRadius | draw the background with topLeft and topRight corner; Only work with fixed tab style |
114 | | style | style to describe the convex shape: **fixed, fixedCircle, react, reactCircle**, ... |
115 | | chipBuilder | custom badge builder, use **ConvexAppBar.badge** for default badge |
116 |
117 | ## Badge
118 |
119 | If you need to add a badge on the tab, use the `ConvexAppBar.badge` to get it done.
120 |
121 | [](doc/badge-demo.mp4 "badge demo")
122 |
123 | ```dart
124 | ConvexAppBar.badge({0: '99+', 1: Icons.assistant_photo, 2: Colors.redAccent},
125 | items: [
126 | TabItem(icon: Icons.home, title: 'Home'),
127 | TabItem(icon: Icons.map, title: 'Discovery'),
128 | TabItem(icon: Icons.add, title: 'Add'),
129 | ],
130 | onTap: (int i) => print('click index=$i'),
131 | );
132 | ```
133 |
134 | The `badge()` method accepts an array of badges; The `badges` is a map with tab items. Each value of entry can be either `String`, `IconData`, `Color` or `Widget`.
135 |
136 | ## Single Button
137 |
138 | If you only need a single button, checkout the `ConvexButton`.
139 |
140 | 
141 |
142 | ```dart
143 | Scaffold(
144 | appBar: AppBar(title: const Text('ConvexButton Example')),
145 | body: Center(child: Text('count $count')),
146 | bottomNavigationBar: ConvexButton.fab(
147 | onTap: () => setState(() => count++),
148 | ),
149 | );
150 | ```
151 |
152 | ## Style Hook
153 | Hook for internal tab style. Unlike the `ConvexAppBar.builder`, you may want to update the tab style without defining a new tab style.
154 |
155 | **Warning:**
156 | This hook is limited and can lead to `overflow broken` if the size you provide does not match with internal style.
157 |
158 | ```dart
159 | StyleProvider(
160 | style: Style(),
161 | child: ConvexAppBar(
162 | initialActiveIndex: 1,
163 | height: 50,
164 | top: -30,
165 | curveSize: 100,
166 | style: TabStyle.fixedCircle,
167 | items: [
168 | TabItem(icon: Icons.link),
169 | TabItem(icon: Icons.import_contacts),
170 | TabItem(title: "2020", icon: Icons.work),
171 | ],
172 | backgroundColor: _tabBackgroundColor,
173 | ),
174 | )
175 | class Style extends StyleHook {
176 | @override
177 | double get activeIconSize => 40;
178 |
179 | @override
180 | double get activeIconMargin => 10;
181 |
182 | @override
183 | double get iconSize => 20;
184 |
185 | @override
186 | TextStyle textStyle(Color color) {
187 | return TextStyle(fontSize: 20, color: color);
188 | }
189 | }
190 | ```
191 |
192 | ## RTL Support
193 | RTL is supported internally, and if you define the TextDirection inside the app, the AppBar should work fine.
194 | Both RTL and LTR can be configured through `Directionality`:
195 | ```dart
196 | Directionality(
197 | textDirection: TextDirection.rtl,
198 | child: Scaffold(body:ConvexAppBar(/*TODO ...*/)),
199 | )
200 | ```
201 |
202 | ## Custom Example
203 |
204 | If the default style does not match your situation, try with `ConvexAppBar.builder()`, allowing you to custom nearly all the tab features.
205 |
206 | ```dart
207 | Scaffold(
208 | bottomNavigationBar: ConvexAppBar.builder(
209 | count: 5,
210 | backgroundColor: Colors.blue,
211 | itemBuilder: Builder(),
212 | )
213 | );
214 |
215 | // user defined class
216 | class Builder extends DelegateBuilder {
217 | @override
218 | Widget build(BuildContext context, int index, bool active) {
219 | return Text('TAB $index');
220 | }
221 | }
222 | ```
223 |
224 | Full custom example can be found at [example](example).
225 |
226 | ## FAQ
227 | Please file feature requests and bugs at the [issue tracker](https://github.com/hacktons/convex_bottom_bar/issues).
228 |
229 | * [How to block tab event?](doc/how-to-block-tab-event.md)
230 | * [Crash on flutter dev/beta channel](doc/issue-crash-on-flutter-dev-channel.md)
231 | * [Change active tab index programmatically](doc/issue-change-active-tab-index.md)
232 | * [Using an image instead of an icon for actionItem](doc/issue-image-for-actionitem.md)
233 | * [Is there anyway to remove elevation in the bottom bar?](doc/issue-remove-elevation.md)
234 |
235 | ## Donate
236 | You like the package ? Buy me a coffee :)
237 |
238 | [](https://ko-fi.com/hacktons)
239 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # Defines a default set of lint rules enforced for
2 | # projects at Google. For details and rationale,
3 | # see https://github.com/dart-lang/pedantic#enabled-lints.
4 | include: package:pedantic/analysis_options.yaml
5 |
6 | linter:
7 | rules:
8 | - public_member_api_docs
9 | analyzer:
10 | exclude:
11 | - example/**
12 | - test/**
13 |
--------------------------------------------------------------------------------
/ci:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | ##################################################################
3 | ##
4 | ## Test case for CI building
5 | ##
6 | ##
7 | ## Author: Chaobin Wu
8 | ## Email : chaobinwu89@gmail.com
9 | ##
10 | #################################################################
11 | tag='[CI]'
12 | error() {
13 | echo -e "\033[1m$tag\033[0m \033[31m$*\033[0m"
14 | }
15 | info() {
16 | echo -e "\033[1m$tag\033[0m \033[32m$*\033[0m"
17 | }
18 | warning() {
19 | echo -e "\033[1m$tag\033[0m \033[33m$*\033[0m"
20 | }
21 |
22 | die() {
23 | error "$*"
24 | exit 1
25 | }
26 | info "Step 1 Run custom lint rules"
27 | dart analyze lib ci-analysis_options.yaml >build/lint-result.txt
28 | # n lints found. => issue found, No issues found! => success
29 | count=$(grep -c 'lints found.' Original designed for [#issue 98](https://github.com/hacktons/convex_bottom_bar/issues/98)
8 |
9 | ## Sample
10 | 
11 |
12 | ```dart
13 | DefaultTabController(
14 | length: items.length,
15 | child: Scaffold(
16 | appBar: AppBar(title: const Text('Custom ConvexAppBar')),
17 | body: TabBarView(
18 | physics: NeverScrollableScrollPhysics(),
19 | children: items.map((i) => Center(child: Text(i.title))).toList(),
20 | ),
21 | bottomNavigationBar: ConvexAppBar(
22 | style: TabStyle.fixedCircle,
23 | items: [
24 | TabItem(title: '2019', icon: Icons.link),
25 | TabItem(
26 | icon: Container(
27 | decoration: BoxDecoration(
28 | shape: BoxShape.circle,
29 | color: Color(0xFFFF5722),
30 | ),
31 | child: Icon(Icons.add, color: Colors.white, size: 40),
32 | )),
33 | TabItem(title: "2020", icon: Icons.work),
34 | ],
35 | onTabNotify: (i) {
36 | var intercept = i == 1;
37 | if (intercept) {
38 | Navigator.pushNamed(context, '/fab');
39 | }
40 | return !intercept;
41 | },
42 | onTap: (i) => debugPrint('click $i'),
43 | ),
44 | ))
45 | ```
46 |
--------------------------------------------------------------------------------
/doc/issue-change-active-tab-index.md:
--------------------------------------------------------------------------------
1 | # Change active tab index programmaticlly
2 |
3 | There ae some cases that you may want to change the activate tab index;
4 | * define a custom initial index
5 | * change index according to PageView/TabBarView
6 |
7 | ## Change initial index
8 |
9 | The `ConvexAppbar` are exposed with `initialActivieIndex`, this value will be used when the appbar are constructed.
10 |
11 | ## Work with PageView/TabBarView
12 | The TabBarView use PageView internal, both support swipe gesture to change current page content;
13 |
14 | 
15 |
16 | ConvexAppBar can work with `TabController` similar with `TabBar`;
17 |
18 | In order to change the index of tab item, config the AppBar with instance of `TabController`; To simplify the code, you can use `DefaultTabController`:
19 |
20 | **Example 1**
21 | ```dart
22 | DefaultTabController(
23 | length: 5,
24 | child: Scaffold(
25 | appBar: AppBar(title: const Text('Custom ConvexAppBar')),
26 | body: TabBarView(
27 | children: ['A','B','C','D','E']
28 | .map((i) => Center(child: Text('$i')))
29 | .toList(growable: false),
30 | ),
31 | bottomNavigationBar: ConvexAppBar(/* some config*/),
32 | ),
33 | );
34 | ```
35 |
36 | **Example 2**
37 | ```dart
38 | Scaffold(
39 | appBar: AppBar(title: const Text('Custom ConvexAppBar')),
40 | body: TabBarView(
41 | controller: _tabController,
42 | children: ['A','B','C','D','E']
43 | .map((i) => Center(child: Text('$i')))
44 | .toList(growable: false),
45 | ),
46 | bottomNavigationBar: ConvexAppBar(controller: _tabController/* some config*/),
47 | );
48 | ```
49 | ## The raw way
50 | If you don't use TabController at all, then you have to update tab index manually through `ConvexAppBarState`.
51 | This usually requires a defined `GlobalKey` set with `ConvexAppBar`:
52 |
53 | ```dart
54 | // define field instance
55 | GlobalKey _appBarKey = GlobalKey();
56 | // construct with key
57 | ConvexAppBar(key: _appBarKey, /* ... */);
58 | // access related State when necessary such as onPageChanged
59 | _appBarKey.currentState.animateTo(2/* index*/);
60 | ```
--------------------------------------------------------------------------------
/doc/issue-crash-on-flutter-dev-channel.md:
--------------------------------------------------------------------------------
1 | # Crash on Flutter dev/beta channel
2 |
3 | As Flutter is developing fast. There can be breaking changes, we will trying to support the
4 | stable version and beta version through different package version.
5 |
6 | If you're using unstable Flutter channel, please follow the table bellow to use the proper package version.
7 |
8 | | **Stable Flutter Version** | **Package Version** | **More** |
9 | | :------------------------: | :-----------------: | :----------------------------------------------------------: |
10 | | >=1.20 | >=2.4.0 | Since v1.20, the stable version changed the Stack api |
11 | | <1.20 | <=2.3.0 | Support for stable version such as v1.17, v1.12 is not going to be updated |
--------------------------------------------------------------------------------
/doc/issue-image-for-actionitem.md:
--------------------------------------------------------------------------------
1 | # Using an image instead of an icon for actionItem
2 |
3 | `TabItem` support both IconData and Widget.
4 |
5 | ```dart
6 | TabItem(icon: Image.asset('images/sample.png'), title: 'Discovery'),
7 | ```
--------------------------------------------------------------------------------
/doc/issue-remove-elevation.md:
--------------------------------------------------------------------------------
1 | # Is there anyway to remove elevation in the bottom bar
2 |
3 | You can remove the shadow by set elevation to 0.
4 |
5 | ```dart
6 | ConvexAppBar(elevation: 0);
7 | ```
8 |
9 |
--------------------------------------------------------------------------------
/doc/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/doc/preview.png
--------------------------------------------------------------------------------
/doc/tab-hook.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/doc/tab-hook.gif
--------------------------------------------------------------------------------
/doc/tabcontroller_pageview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/doc/tabcontroller_pageview.gif
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .packages
28 | .pub-cache/
29 | .pub/
30 | /build/
31 |
32 | # Android related
33 | **/android/**/gradle-wrapper.jar
34 | **/android/.gradle
35 | **/android/captures/
36 | **/android/gradlew
37 | **/android/gradlew.bat
38 | **/android/local.properties
39 | **/android/**/GeneratedPluginRegistrant.java
40 |
41 | # iOS/XCode related
42 | **/ios/**/*.mode1v3
43 | **/ios/**/*.mode2v3
44 | **/ios/**/*.moved-aside
45 | **/ios/**/*.pbxuser
46 | **/ios/**/*.perspectivev3
47 | **/ios/**/*sync/
48 | **/ios/**/.sconsign.dblite
49 | **/ios/**/.tags*
50 | **/ios/**/.vagrant/
51 | **/ios/**/DerivedData/
52 | **/ios/**/Icon?
53 | **/ios/**/Pods/
54 | **/ios/**/.symlinks/
55 | **/ios/**/profile
56 | **/ios/**/xcuserdata
57 | **/ios/.generated/
58 | **/ios/Flutter/App.framework
59 | **/ios/Flutter/Flutter.framework
60 | **/ios/Flutter/Generated.xcconfig
61 | **/ios/Flutter/app.flx
62 | **/ios/Flutter/app.zip
63 | **/ios/Flutter/flutter_assets/
64 | **/ios/Flutter/flutter_export_environment.sh
65 | **/ios/ServiceDefinitions.json
66 | **/ios/Runner/GeneratedPluginRegistrant.*
67 |
68 | # Exceptions to above rules.
69 | !**/ios/**/default.mode1v3
70 | !**/ios/**/default.mode2v3
71 | !**/ios/**/default.pbxuser
72 | !**/ios/**/default.perspectivev3
73 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
74 |
75 | pubspec.lock
--------------------------------------------------------------------------------
/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: cc949a8e8b9cf394b9290a8e80f87af3e207dce5
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # convex_bottom_bar_example
2 |
3 | Demonstrates how to use the convex_bottom_bar plugin.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
13 |
14 | For help getting started with Flutter, view our
15 | [online documentation](https://flutter.dev/docs), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion flutter.compileSdkVersion
30 | ndkVersion flutter.ndkVersion
31 |
32 | compileOptions {
33 | sourceCompatibility JavaVersion.VERSION_1_8
34 | targetCompatibility JavaVersion.VERSION_1_8
35 | }
36 |
37 | kotlinOptions {
38 | jvmTarget = '1.8'
39 | }
40 |
41 | sourceSets {
42 | main.java.srcDirs += 'src/main/kotlin'
43 | }
44 |
45 | defaultConfig {
46 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
47 | applicationId "com.example.convexappbar"
48 | // You can update the following values to match your application needs.
49 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
50 | minSdkVersion flutter.minSdkVersion
51 | targetSdkVersion flutter.targetSdkVersion
52 | versionCode flutterVersionCode.toInteger()
53 | versionName flutterVersionName
54 | }
55 |
56 | buildTypes {
57 | release {
58 | // TODO: Add your own signing config for the release build.
59 | // Signing with the debug keys for now, so `flutter run --release` works.
60 | signingConfig signingConfigs.debug
61 | }
62 | }
63 | }
64 |
65 | flutter {
66 | source '../..'
67 | }
68 |
69 | dependencies {
70 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
71 | }
72 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
15 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
30 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/com/example/convexappbar/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.convexappbar
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.6.10'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.1.2'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
7 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/example/images/sample-1-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/images/sample-1-2.png
--------------------------------------------------------------------------------
/example/images/sample-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/images/sample-1.png
--------------------------------------------------------------------------------
/example/images/sample-2-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/images/sample-2-2.png
--------------------------------------------------------------------------------
/example/images/sample-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/images/sample-2.png
--------------------------------------------------------------------------------
/example/images/sample-3-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/images/sample-3-2.png
--------------------------------------------------------------------------------
/example/images/sample-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/images/sample-3.png
--------------------------------------------------------------------------------
/example/images/sample-4-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/images/sample-4-2.png
--------------------------------------------------------------------------------
/example/images/sample-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/images/sample-4.png
--------------------------------------------------------------------------------
/example/images/sample-5-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/images/sample-5-2.png
--------------------------------------------------------------------------------
/example/images/sample-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/images/sample-5.png
--------------------------------------------------------------------------------
/example/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | 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.
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | AppBarDemo
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/example/lib/color_item_view.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 chaobinwu89@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/cupertino.dart';
18 | import 'package:flutter/material.dart';
19 |
20 | class ColorItemView extends StatelessWidget {
21 | final Color color;
22 |
23 | ColorItemView(this.color);
24 |
25 | @override
26 | Widget build(BuildContext context) {
27 | return Container(
28 | color: color,
29 | alignment: Alignment.center,
30 | child: Text(
31 | color.value.toRadixString(16).toUpperCase(),
32 | style: const TextStyle(color: Colors.white),
33 | ),
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/example/lib/components/chip_item.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 chaobinwu89@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/cupertino.dart';
18 | import 'package:flutter/material.dart';
19 |
20 | import '../model/badge.dart';
21 |
22 | class ChipItem extends StatelessWidget {
23 | const ChipItem(this.chips, this.selectedChip, this.onChanged);
24 |
25 | final List chips;
26 | final SampleBadge? selectedChip;
27 | final ValueChanged onChanged;
28 |
29 | @override
30 | Widget build(BuildContext context) {
31 | return Row(
32 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
33 | children: chips.map((SampleBadge? chip) {
34 | return GestureDetector(
35 | onTap: () => onChanged(chip),
36 | child: Container(
37 | height: 40,
38 | width: 80,
39 | padding: EdgeInsets.all(8.0),
40 | decoration: BoxDecoration(
41 | border: Border.all(
42 | color: chip == selectedChip
43 | ? Colors.black
44 | : const Color(0xFFD5D7DA),
45 | width: 2),
46 | ),
47 | child: chip == null
48 | ? Center(child: Text('clear'))
49 | : Center(
50 | child: Material(
51 | shape: RoundedRectangleBorder(
52 | borderRadius:
53 | BorderRadius.circular(chip.borderRadius ?? 20),
54 | ),
55 | type: MaterialType.card,
56 | color: chip.badgeColor ?? Colors.redAccent,
57 | child: Padding(
58 | padding:
59 | chip.padding ?? EdgeInsets.only(left: 4, right: 4),
60 | child: Text(
61 | chip.text,
62 | style: const TextStyle(color: Colors.white),
63 | ),
64 | ),
65 | ),
66 | ),
67 | ),
68 | );
69 | }).toList(),
70 | );
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/example/lib/components/choose_tab_item.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 chaobinwu89@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:convex_bottom_bar/convex_bottom_bar.dart';
18 | import 'package:flutter/cupertino.dart';
19 | import 'package:flutter/material.dart';
20 |
21 | import '../model/choice_value.dart';
22 | import 'radio_item.dart';
23 |
24 | class ChooseTabItem extends StatelessWidget {
25 | const ChooseTabItem(this.tabTypes, this.selectedType, this.onChanged);
26 |
27 | final List>> tabTypes;
28 | final ChoiceValue> selectedType;
29 | final ValueChanged>?>? onChanged;
30 |
31 | @override
32 | Widget build(BuildContext context) {
33 | return Row(
34 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
35 | children: tabTypes.map((ChoiceValue> type) {
36 | return Expanded(
37 | child: RadioItem>(type, selectedType, onChanged),
38 | );
39 | }).toList(),
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/example/lib/components/colors_item.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 chaobinwu89@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/cupertino.dart';
18 | import 'package:flutter/material.dart';
19 |
20 | import '../model/named_color.dart';
21 |
22 | class ColorsItem extends StatelessWidget {
23 | const ColorsItem(this.colors, this.selectedColor, this.onChanged);
24 |
25 | final List colors;
26 | final Color selectedColor;
27 | final ValueChanged onChanged;
28 |
29 | @override
30 | Widget build(BuildContext context) {
31 | return Row(
32 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
33 | children: colors.map((NamedColor namedColor) {
34 | return RawMaterialButton(
35 | onPressed: () {
36 | onChanged(namedColor.color);
37 | },
38 | constraints: const BoxConstraints.tightFor(
39 | width: 32.0,
40 | height: 32.0,
41 | ),
42 | fillColor: namedColor.color,
43 | shape: CircleBorder(
44 | side: BorderSide(
45 | color: namedColor.color == selectedColor
46 | ? Colors.black
47 | : const Color(0xFFD5D7DA),
48 | width: 2.0,
49 | ),
50 | ),
51 | child: Semantics(
52 | value: namedColor.name,
53 | selected: namedColor.color == selectedColor,
54 | ),
55 | );
56 | }).toList(),
57 | );
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/example/lib/components/gradient_item.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 chaobinwu89@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/cupertino.dart';
18 | import 'package:flutter/material.dart';
19 |
20 | class GradientItem extends StatelessWidget {
21 | const GradientItem(this.colors, this.selectedColor, this.onChanged);
22 |
23 | final List colors;
24 | final Gradient? selectedColor;
25 | final ValueChanged onChanged;
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | return Row(
30 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
31 | children: colors.map((Gradient? namedColor) {
32 | return GestureDetector(
33 | onTap: () => onChanged(namedColor),
34 | child: Container(
35 | height: 40,
36 | width: 80,
37 | padding: EdgeInsets.all(8.0),
38 | decoration: BoxDecoration(
39 | border: Border.all(
40 | color: namedColor == selectedColor
41 | ? Colors.black
42 | : const Color(0xFFD5D7DA),
43 | width: 2),
44 | gradient: namedColor,
45 | color: namedColor != null ? Colors.grey : null,
46 | ),
47 | child: namedColor == null ? Center(child: Text('clear')) : null,
48 | ),
49 | );
50 | }).toList(),
51 | );
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/example/lib/components/heading.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 chaobinwu89@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/cupertino.dart';
18 | import 'package:flutter/material.dart';
19 |
20 | class Heading extends StatelessWidget {
21 | const Heading(this.text);
22 |
23 | final String text;
24 |
25 | @override
26 | Widget build(BuildContext context) {
27 | final ThemeData theme = Theme.of(context);
28 | return Container(
29 | height: 48.0,
30 | padding: const EdgeInsetsDirectional.only(start: 56.0),
31 | alignment: AlignmentDirectional.centerStart,
32 | child: Text(
33 | text,
34 | style: TextStyle(
35 | color: theme.primaryColor,
36 | ),
37 | ),
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/example/lib/components/radio_item.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 chaobinwu89@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/cupertino.dart';
18 | import 'package:flutter/material.dart';
19 |
20 | import '../model/choice_value.dart';
21 |
22 | // copy of _RadioItem from flutter gallery
23 | class RadioItem extends StatelessWidget {
24 | const RadioItem(this.value, this.groupValue, this.onChanged);
25 |
26 | final ChoiceValue value;
27 | final ChoiceValue groupValue;
28 | final ValueChanged?>? onChanged;
29 |
30 | @override
31 | Widget build(BuildContext context) {
32 | return Container(
33 | height: 56.0,
34 | padding: const EdgeInsetsDirectional.only(start: 16.0),
35 | alignment: AlignmentDirectional.centerStart,
36 | child: MergeSemantics(
37 | child: Row(
38 | children: [
39 | Radio>(
40 | value: value,
41 | groupValue: groupValue,
42 | onChanged: onChanged,
43 | ),
44 | Expanded(
45 | child: Semantics(
46 | container: true,
47 | button: true,
48 | label: value.label,
49 | child: GestureDetector(
50 | behavior: HitTestBehavior.opaque,
51 | onTap: () {
52 | if (onChanged != null) {
53 | onChanged!(value);
54 | }
55 | },
56 | child: Text(value.title),
57 | ),
58 | ),
59 | ),
60 | ],
61 | ),
62 | ),
63 | );
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/example/lib/convex_button_demo.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:convex_bottom_bar/convex_bottom_bar.dart';
18 | import 'package:flutter/cupertino.dart';
19 | import 'package:flutter/material.dart';
20 |
21 | class ConvexButtonDemo extends StatefulWidget {
22 | @override
23 | State createState() {
24 | return _State();
25 | }
26 | }
27 |
28 | class _State extends State {
29 | int count = 0;
30 |
31 | @override
32 | Widget build(BuildContext context) {
33 | return Scaffold(
34 | appBar: AppBar(title: const Text('ConvexButton Example')),
35 | body: Center(child: Text('count $count')),
36 | bottomNavigationBar: ConvexButton.fab(
37 | onTap: () => setState(() => count++),
38 | ),
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/example/lib/custom_appbar_sample.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 chaobinwu89@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:convex_bottom_bar/convex_bottom_bar.dart';
18 | import 'package:flutter/material.dart';
19 |
20 | import 'color_item_view.dart';
21 |
22 | class CustomAppBarDemo extends StatefulWidget {
23 | @override
24 | State createState() {
25 | return _State();
26 | }
27 | }
28 |
29 | class _State extends State
30 | with SingleTickerProviderStateMixin {
31 | List items = [
32 | TabItem(icon: Icons.home, title: 'Home'),
33 | TabItem(icon: Icons.map, title: 'Discovery'),
34 | TabItem(icon: Icons.plus_one, title: 'Add'),
35 | ];
36 |
37 | static const paletteColors = [
38 | Color(0xFFf44336),
39 | Color(0xFFE91E63),
40 | Color(0xFF9C27B0),
41 | Color(0xFF673AB7),
42 | Color(0xFF3F51B5),
43 | Color(0xFF2196F3),
44 | Color(0xFF00BCD4),
45 | Color(0xFF009688),
46 | Color(0xFF4CAF50),
47 | Color(0xFF8BC34A),
48 | Color(0xFFCDDC39),
49 | Color(0xFFFFEB3B),
50 | Color(0xFFFFC107),
51 | Color(0xFFFF9800),
52 | Color(0xFFFF5722),
53 | Color(0xFF795548),
54 | Color(0xFF9E9E9E),
55 | Color(0xFF607D8B),
56 | ];
57 | Color _tabBackgroundColor = paletteColors[5];
58 |
59 | @override
60 | Widget build(BuildContext context) {
61 | return DefaultTabController(
62 | initialIndex: 0,
63 | length: items.length,
64 | child: Scaffold(
65 | appBar: AppBar(title: const Text('Custom ConvexAppBar')),
66 | body: TabBarView(
67 | physics: NeverScrollableScrollPhysics(),
68 | children: items
69 | .map((i) => i.title == 'Discovery'
70 | ? paletteBody()
71 | : Center(
72 | child: Text(
73 | '<\t\t${i.title}\t\t>',
74 | style: TextStyle(fontSize: 30),
75 | )))
76 | .toList(growable: false),
77 | ),
78 | bottomNavigationBar: StyleProvider(
79 | style: Style(),
80 | child: ConvexAppBar(
81 | disableDefaultTabController: true,
82 | initialActiveIndex: 0,
83 | height: 50,
84 | top: -30,
85 | curveSize: 100,
86 | style: TabStyle.fixedCircle,
87 | items: [
88 | TabItem(title: '2019', icon: Icons.link),
89 | TabItem(
90 | icon: Container(
91 | decoration: BoxDecoration(
92 | shape: BoxShape.circle,
93 | color: Color(0xFFFF5722),
94 | ),
95 | child: Icon(Icons.add, color: Colors.white, size: 40),
96 | )),
97 | TabItem(title: "2020", icon: Icons.work),
98 | ],
99 | backgroundColor: _tabBackgroundColor,
100 | cornerRadius: 25,
101 | onTabNotify: (i) {
102 | var intercept = i == 1;
103 | if (intercept) {
104 | Navigator.pushNamed(context, '/fab');
105 | }
106 | return !intercept;
107 | },
108 | onTap: (i) => debugPrint('click $i'),
109 | ),
110 | ),
111 | ));
112 | }
113 |
114 | Widget builder() {
115 | return ConvexAppBar.builder(
116 | itemBuilder: _CustomBuilder(items, _tabBackgroundColor),
117 | count: items.length,
118 | backgroundColor: _tabBackgroundColor,
119 | );
120 | }
121 |
122 | Container tabContent(TabItem data, Color color) {
123 | return Container(
124 | height: 50,
125 | padding: EdgeInsets.only(bottom: 2),
126 | child: Column(
127 | mainAxisAlignment: MainAxisAlignment.end,
128 | children: [
129 | Icon(data.icon, color: color),
130 | Text(data.title != null ? data.title! : "",
131 | style: TextStyle(color: color))
132 | ],
133 | ));
134 | }
135 |
136 | GridView paletteBody() {
137 | return GridView.count(
138 | crossAxisCount: 5,
139 | childAspectRatio: 1,
140 | mainAxisSpacing: 1,
141 | crossAxisSpacing: 1,
142 | children: paletteColors
143 | .map((c) => GestureDetector(
144 | child: ColorItemView(c),
145 | onTap: () => _onColorChanged(c),
146 | ))
147 | .toList(),
148 | );
149 | }
150 |
151 | void _onColorChanged(Color color) {
152 | setState(() {
153 | _tabBackgroundColor = color;
154 | });
155 | }
156 | }
157 |
158 | class _CustomBuilder extends DelegateBuilder {
159 | final List items;
160 | final Color _tabBackgroundColor;
161 |
162 | _CustomBuilder(this.items, this._tabBackgroundColor);
163 |
164 | @override
165 | Widget build(BuildContext context, int index, bool active) {
166 | var navigationItem = items[index];
167 | var _color = active ? Colors.white : Colors.white60;
168 |
169 | if (index == items.length ~/ 2) {
170 | return Container(
171 | width: 60,
172 | height: 60,
173 | margin: EdgeInsets.all(5),
174 | decoration: BoxDecoration(shape: BoxShape.circle, color: _color),
175 | child: Icon(
176 | Icons.add,
177 | size: 40,
178 | color: _tabBackgroundColor,
179 | ),
180 | );
181 | }
182 | var _icon = active
183 | ? navigationItem.activeIcon ?? navigationItem.icon
184 | : navigationItem.icon;
185 | var _title = navigationItem.title ?? "";
186 | return Container(
187 | color: Colors.transparent,
188 | padding: EdgeInsets.only(bottom: 2),
189 | child: Column(
190 | mainAxisAlignment: MainAxisAlignment.end,
191 | children: [
192 | Icon(_icon, color: _color),
193 | Text(_title, style: TextStyle(color: _color))
194 | ],
195 | ),
196 | );
197 | }
198 |
199 | @override
200 | bool fixed() {
201 | return true;
202 | }
203 | }
204 |
205 | class Style extends StyleHook {
206 | @override
207 | double get activeIconSize => 40;
208 |
209 | @override
210 | double get activeIconMargin => 10;
211 |
212 | @override
213 | double get iconSize => 20;
214 |
215 | @override
216 | TextStyle textStyle(Color color, String? fontFamily) {
217 | return TextStyle(fontSize: 20, color: color, fontFamily: fontFamily);
218 | }
219 | }
220 |
--------------------------------------------------------------------------------
/example/lib/data.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 chaobinwu89@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:convex_bottom_bar/convex_bottom_bar.dart';
18 | import 'package:flutter/cupertino.dart';
19 | import 'package:flutter/material.dart';
20 |
21 | import 'model/badge.dart';
22 | import 'model/choice_value.dart';
23 | import 'model/named_color.dart';
24 |
25 | /// tab config used in example
26 | class Data {
27 | static const gradients = [
28 | null,
29 | LinearGradient(
30 | begin: Alignment.topLeft,
31 | end: Alignment.bottomRight,
32 | colors: [Colors.blue, Colors.redAccent, Colors.green, Colors.blue],
33 | tileMode: TileMode.repeated,
34 | ),
35 | LinearGradient(
36 | begin: Alignment.center,
37 | end: Alignment(-1, 1),
38 | colors: [Colors.redAccent, Colors.green, Colors.blue],
39 | tileMode: TileMode.repeated,
40 | ),
41 | RadialGradient(
42 | center: const Alignment(0, 0), // near the top right
43 | radius: 5,
44 | colors: [Colors.green, Colors.blue, Colors.redAccent],
45 | )
46 | ];
47 |
48 | static const namedColors = [
49 | NamedColor(Colors.blue, 'Blue'),
50 | NamedColor(Color(0xFFf44336), 'Red'),
51 | NamedColor(Color(0xFF673AB7), 'Purple'),
52 | NamedColor(Color(0xFF009688), 'Green'),
53 | NamedColor(Color(0xFFFFC107), 'Yellow'),
54 | NamedColor(Color(0xFF607D8B), 'Grey'),
55 | ];
56 | static const namedShadowColors = [
57 | NamedColor(Colors.black38, 'Black'),
58 | NamedColor(Color(0xeef44336), 'Red'),
59 | NamedColor(Color(0xee673AB7), 'Purple'),
60 | NamedColor(Color(0xee009688), 'Green'),
61 | NamedColor(Color(0xeeFFC107), 'Yellow'),
62 | NamedColor(Color(0xee607D8B), 'Grey'),
63 | ];
64 | static const badges = [
65 | null,
66 | SampleBadge('1'),
67 | SampleBadge('hot',
68 | badgeColor: Colors.orange, padding: EdgeInsets.only(left: 7, right: 7)),
69 | SampleBadge('99+', borderRadius: 2)
70 | ];
71 |
72 | static const curves = [
73 | ChoiceValue(
74 | title: 'Curves.bounceInOut',
75 | label: 'The curve bounceInOut is used',
76 | value: Curves.bounceInOut,
77 | ),
78 | ChoiceValue(
79 | title: 'Curves.decelerate',
80 | value: Curves.decelerate,
81 | label: 'The curve decelerate is used',
82 | ),
83 | ChoiceValue(
84 | title: 'Curves.easeInOut',
85 | value: Curves.easeInOut,
86 | label: 'The curve easeInOut is used',
87 | ),
88 | ChoiceValue(
89 | title: 'Curves.fastOutSlowIn',
90 | value: Curves.fastOutSlowIn,
91 | label: 'The curve fastOutSlowIn is used',
92 | ),
93 | ];
94 |
95 | static List items({bool image = false}) {
96 | if (image) {
97 | return [
98 | TabItem(
99 | icon: Image.asset('images/sample-1.png'),
100 | activeIcon: Image.asset('images/sample-1-2.png'),
101 | title: 'Happy',
102 | ),
103 | TabItem(
104 | icon: Image.asset('images/sample-2.png'),
105 | activeIcon: Image.asset('images/sample-2-2.png'),
106 | title: 'New'),
107 | TabItem(
108 | icon: Image.asset('images/sample-3.png'),
109 | activeIcon: Image.asset('images/sample-3-2.png'),
110 | title: 'Year',
111 | ),
112 | TabItem(
113 | icon: Image.asset('images/sample-4.png'),
114 | activeIcon: Image.asset('images/sample-4-2.png'),
115 | title: '20',
116 | ),
117 | TabItem(
118 | icon: Image.asset('images/sample-5.png'),
119 | activeIcon: Image.asset('images/sample-5-2.png'),
120 | title: '20',
121 | ),
122 | ];
123 | }
124 | return [
125 | TabItem(icon: Icons.home, title: 'Home'),
126 | TabItem(icon: Icons.map, title: "Discovery"),
127 | TabItem(icon: Icons.publish, title: "Publish"),
128 | TabItem(icon: Icons.message, title: 'Message'),
129 | TabItem(icon: Icons.people, title: 'Profile'),
130 | ];
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/example/lib/default_appbar_demo.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 chaobinwu89@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:convex_bottom_bar/convex_bottom_bar.dart';
18 | import 'package:flutter/foundation.dart';
19 | import 'package:flutter/material.dart';
20 | import 'package:flutter/rendering.dart';
21 | import 'package:flutter/widgets.dart';
22 |
23 | import 'components/chip_item.dart';
24 | import 'components/choose_tab_item.dart';
25 | import 'components/colors_item.dart';
26 | import 'components/gradient_item.dart';
27 | import 'components/heading.dart';
28 | import 'components/radio_item.dart';
29 | import 'data.dart';
30 | import 'model/badge.dart';
31 | import 'model/choice_value.dart';
32 |
33 | class DefaultAppBarDemo extends StatefulWidget {
34 | @override
35 | State createState() {
36 | return _State();
37 | }
38 | }
39 |
40 | class _State extends State
41 | with SingleTickerProviderStateMixin {
42 | static const kStyles = [
43 | ChoiceValue(
44 | title: 'TabStyle.react',
45 | label: 'Appbar use react style',
46 | value: TabStyle.react,
47 | ),
48 | ChoiceValue(
49 | title: 'TabStyle.reactCircle',
50 | label: 'Appbar use reactCircle style',
51 | value: TabStyle.reactCircle,
52 | ),
53 | ChoiceValue(
54 | title: kIsWeb
55 | ? 'TabStyle.flip (Flutter Web is not supported)'
56 | : 'TabStyle.flip',
57 | label: 'Appbar use flip style',
58 | value: TabStyle.flip,
59 | ),
60 | ChoiceValue(
61 | title: 'TabStyle.textIn',
62 | label: 'Appbar use textIn style',
63 | value: TabStyle.textIn,
64 | ),
65 | ChoiceValue(
66 | title: 'TabStyle.titled',
67 | label: 'Appbar use titled style',
68 | value: TabStyle.titled,
69 | ),
70 | ChoiceValue(
71 | title: 'TabStyle.fixed',
72 | label: 'Appbar use fixed style',
73 | value: TabStyle.fixed,
74 | ),
75 | ChoiceValue(
76 | title: 'TabStyle.fixedCircle',
77 | label: 'Appbar use fixedCircle style',
78 | value: TabStyle.fixedCircle,
79 | ),
80 | ];
81 |
82 | static final kTabTypes = [
83 | ChoiceValue>(
84 | title: 'Icon Tab',
85 | label: 'Appbar use icon with Tab',
86 | value: Data.items(image: false),
87 | ),
88 | ChoiceValue>(
89 | title: 'Image Tab',
90 | label: 'Appbar use image with Tab',
91 | value: Data.items(image: true),
92 | ),
93 | ];
94 | var _tabItems = kTabTypes.first;
95 |
96 | ChoiceValue _style = kStyles.first;
97 | ChoiceValue _curve = Data.curves.first;
98 | Color _barColor = Data.namedColors.first.color;
99 | Color _shadowColor = Data.namedColors.first.color;
100 | Gradient? _gradient = Data.gradients.first;
101 | SampleBadge? _badge;
102 | TabController? _tabController;
103 | TextDirection _textDirection = TextDirection.ltr;
104 |
105 | @override
106 | void initState() {
107 | super.initState();
108 | _tabController = TabController(length: _tabItems.value.length, vsync: this);
109 | }
110 |
111 | @override
112 | Widget build(BuildContext context) {
113 | var options = [
114 | const Heading('Appbar Color'),
115 | ColorsItem(Data.namedColors, _barColor, _onBarColorChanged),
116 | const Heading('Shadow Color'),
117 | ColorsItem(Data.namedShadowColors, _shadowColor, _onShadowColorChanged),
118 | const Heading('Background Gradient'),
119 | GradientItem(Data.gradients, _gradient, _onGradientChanged),
120 | const Heading('Badge Chip'),
121 | ChipItem(Data.badges, _badge, _onBadgeChanged),
122 | const Heading('Tab Type'),
123 | ChooseTabItem(kTabTypes, _tabItems, _onTabItemTypeChanged),
124 | const Heading('Tab Style'),
125 | ];
126 | options.addAll(kStyles.map((s) => RadioItem(s, _style,
127 | s.value == TabStyle.flip && kIsWeb ? _onNothing : _onStyleChanged)));
128 | if (_style.value != TabStyle.fixed &&
129 | _style.value != TabStyle.fixedCircle) {
130 | options.add(const Heading('Animation Curve'));
131 | options.addAll(
132 | Data.curves.map((c) => RadioItem(c, _curve, _onCurveChanged)));
133 | }
134 |
135 | return Directionality(
136 | textDirection: _textDirection,
137 | child: Scaffold(
138 | appBar: AppBar(
139 | title: const Text('ConvexAppBar'),
140 | backgroundColor: _barColor,
141 | actions: [
142 | IconButton(
143 | icon: Icon(_textDirection == TextDirection.rtl
144 | ? Icons.format_textdirection_r_to_l
145 | : Icons.format_textdirection_l_to_r),
146 | color: Colors.white,
147 | tooltip: _textDirection == TextDirection.rtl
148 | ? "Change to LTR"
149 | : "Change to RTL",
150 | onPressed: () {
151 | setState(() {
152 | _textDirection = _textDirection == TextDirection.ltr
153 | ? TextDirection.rtl
154 | : TextDirection.ltr;
155 | });
156 | },
157 | ),
158 | IconButton(
159 | icon: Icon(Icons.style),
160 | color: Colors.white,
161 | tooltip: "Custom style example",
162 | onPressed: () => Navigator.of(context).pushNamed('/custom'),
163 | ),
164 | IconButton(
165 | icon: Icon(Icons.radio_button_checked),
166 | color: Colors.white,
167 | tooltip: "convex button example",
168 | onPressed: () => Navigator.of(context).pushNamed('/fab'),
169 | ),
170 | IconButton(
171 | icon: Icon(Icons.looks_two),
172 | color: Colors.white,
173 | tooltip: "change tab by controller",
174 | onPressed: () {
175 | _tabController?.animateTo(2);
176 | },
177 | )
178 | ],
179 | ),
180 | body: TabBarView(
181 | controller: _tabController,
182 | children: _tabItems.value
183 | .map((i) => i.title == 'Home' || i.title == 'Happy'
184 | ? ListView(children: options)
185 | : Center(
186 | child: Text(
187 | '${i.title} World',
188 | style: TextStyle(fontSize: 30),
189 | )))
190 | .toList(growable: false)),
191 | bottomNavigationBar: _badge == null
192 | ? ConvexAppBar(
193 | items: _tabItems.value,
194 | style: _style.value,
195 | curve: _curve.value,
196 | shadowColor: _shadowColor,
197 | backgroundColor: _barColor,
198 | gradient: _gradient,
199 | controller: _tabController,
200 | onTap: (int i) => debugPrint('select index=$i'),
201 | )
202 | : ConvexAppBar.badge(
203 | {
204 | 3: _badge!.text,
205 | 4: Icons.assistant_photo,
206 | 2: Colors.redAccent
207 | },
208 | badgePadding: _badge!.padding,
209 | badgeColor: _badge!.badgeColor,
210 | badgeBorderRadius: _badge!.borderRadius,
211 | badgeMargin: EdgeInsets.only(bottom: 20, left: 30),
212 | items: _tabItems.value,
213 | style: _style.value,
214 | curve: _curve.value,
215 | shadowColor: _shadowColor,
216 | backgroundColor: _barColor,
217 | gradient: _gradient,
218 | controller: _tabController,
219 | onTap: (int i) => debugPrint('select index=$i'),
220 | ),
221 | ),
222 | );
223 | }
224 |
225 | void _onTabItemTypeChanged(ChoiceValue>? value) {
226 | if (value == null) {
227 | return;
228 | }
229 | setState(() {
230 | _tabItems = value;
231 | });
232 | }
233 |
234 | void _onNothing(ChoiceValue? value) {}
235 |
236 | void _onStyleChanged(ChoiceValue? value) {
237 | if (value == null) {
238 | return;
239 | }
240 | setState(() {
241 | _style = value;
242 | });
243 | }
244 |
245 | void _onCurveChanged(ChoiceValue? value) {
246 | if (value == null) {
247 | return;
248 | }
249 | setState(() {
250 | _curve = value;
251 | });
252 | }
253 |
254 | void _onBarColorChanged(Color value) {
255 | setState(() {
256 | _barColor = value;
257 | });
258 | }
259 |
260 | void _onShadowColorChanged(Color value) {
261 | setState(() {
262 | _shadowColor = value;
263 | });
264 | }
265 |
266 | void _onGradientChanged(Gradient? value) {
267 | setState(() {
268 | _gradient = value;
269 | });
270 | }
271 |
272 | void _onBadgeChanged(SampleBadge? value) {
273 | setState(() {
274 | _badge = value;
275 | });
276 | }
277 | }
278 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 chaobinwu89@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:convex_app_bar_example/convex_button_demo.dart';
18 | import 'package:convex_app_bar_example/custom_appbar_sample.dart';
19 | import 'package:convex_bottom_bar/convex_bottom_bar.dart';
20 | import 'package:flutter/material.dart';
21 |
22 | import 'default_appbar_demo.dart';
23 |
24 | void main() => runApp(MyApp());
25 |
26 | class MyApp extends StatefulWidget {
27 | @override
28 | State createState() => _State();
29 | }
30 |
31 | class _State extends State {
32 | @override
33 | Widget build(BuildContext context) {
34 | return MaterialApp(
35 | initialRoute: "/",
36 | routes: {
37 | "/": (_) => HelloConvexAppBar(),
38 | "/bar": (BuildContext context) => DefaultAppBarDemo(),
39 | "/custom": (BuildContext context) => CustomAppBarDemo(),
40 | "/fab": (BuildContext context) => ConvexButtonDemo(),
41 | },
42 | );
43 | }
44 | }
45 |
46 | class HelloConvexAppBar extends StatelessWidget {
47 | @override
48 | Widget build(BuildContext context) {
49 | return Scaffold(
50 | appBar: AppBar(title: Text('Hello ConvexAppBar')),
51 | body: Center(
52 | child: TextButton(
53 | child: Text('Click to show full example'),
54 | onPressed: () => Navigator.of(context).pushNamed('/bar'),
55 | )),
56 | bottomNavigationBar: ConvexAppBar(
57 | style: TabStyle.react,
58 | items: [
59 | TabItem(icon: Icons.list),
60 | TabItem(icon: Icons.calendar_today),
61 | TabItem(icon: Icons.assessment),
62 | ],
63 | initialActiveIndex: 1,
64 | onTap: (int i) => print('click index=$i'),
65 | ),
66 | );
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/example/lib/model/badge.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 chaobinwu89@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/material.dart';
18 |
19 | class SampleBadge {
20 | final Color? color;
21 | final Color? badgeColor;
22 | final EdgeInsets? padding;
23 | final double? borderRadius;
24 | final String text;
25 |
26 | const SampleBadge(
27 | this.text, {
28 | this.color,
29 | this.badgeColor,
30 | this.padding,
31 | this.borderRadius,
32 | });
33 | }
34 |
--------------------------------------------------------------------------------
/example/lib/model/choice_value.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 chaobinwu89@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // copy of _ChoiceValue from flutter gallery
18 | class ChoiceValue {
19 | const ChoiceValue(
20 | {required this.value, required this.title, required this.label});
21 |
22 | final T value;
23 | final String title;
24 | final String label; // For the Semantics widget that contains title
25 |
26 | @override
27 | String toString() => '$runtimeType("$title")';
28 | }
29 |
--------------------------------------------------------------------------------
/example/lib/model/named_color.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 chaobinwu89@gmail.com
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'dart:ui';
18 |
19 | class NamedColor {
20 | const NamedColor(this.color, this.name);
21 |
22 | final Color color;
23 | final String name;
24 | }
25 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: convex_app_bar_example
2 | description: Demonstrates how to use the convex_app_bar plugin.
3 | publish_to: 'none'
4 | version: 1.0.0
5 |
6 | environment:
7 | sdk: ">=2.15.0 <3.0.0"
8 |
9 | dependencies:
10 | flutter:
11 | sdk: flutter
12 |
13 | # The following adds the Cupertino Icons font to your application.
14 | # Use with the CupertinoIcons class for iOS style icons.
15 | cupertino_icons: ^0.1.2
16 |
17 | dev_dependencies:
18 | flutter_test:
19 | sdk: flutter
20 |
21 | convex_bottom_bar:
22 | path: ../
23 |
24 | # For information on the generic Dart part of this file, see the
25 | # following page: https://dart.dev/tools/pub/pubspec
26 |
27 | # The following section is specific to Flutter.
28 | flutter:
29 |
30 | # The following line ensures that the Material Icons font is
31 | # included with your application, so that you can use the icons in
32 | # the material Icons class.
33 | uses-material-design: true
34 |
35 | # To add assets to your application, add an assets section, like this:
36 | assets:
37 | - images/
38 |
39 | # An image asset can refer to one or more resolution-specific "variants", see
40 | # https://flutter.dev/assets-and-images/#resolution-aware.
41 |
42 | # For details regarding adding assets from package dependencies, see
43 | # https://flutter.dev/assets-and-images/#from-packages
44 |
45 | # To add custom fonts to your application, add a fonts section here,
46 | # in this "flutter" section. Each entry in this list should have a
47 | # "family" key with the font family name, and a "fonts" key with a
48 | # list giving the asset and other descriptors for the font. For
49 | # example:
50 | # fonts:
51 | # - family: Schyler
52 | # fonts:
53 | # - asset: fonts/Schyler-Regular.ttf
54 | # - asset: fonts/Schyler-Italic.ttf
55 | # style: italic
56 | # - family: Trajan Pro
57 | # fonts:
58 | # - asset: fonts/TrajanPro.ttf
59 | # - asset: fonts/TrajanPro_Bold.ttf
60 | # weight: 700
61 | #
62 | # For details regarding fonts from package dependencies,
63 | # see https://flutter.dev/custom-fonts/#from-packages
64 |
--------------------------------------------------------------------------------
/example/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/web/favicon.png
--------------------------------------------------------------------------------
/example/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/example/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hacktons/convex_bottom_bar/ddb58b0216eee90c3e88f36eda58d1ba1646ce96/example/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/example/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ConvexAppBar Example
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "short_name": "example",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#0175C2",
7 | "theme_color": "#0175C2",
8 | "description": "A new Flutter project.",
9 | "orientation": "portrait-primary",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/Icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/Icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/lib/convex_bottom_bar.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | library convex_appbar;
18 |
19 | export 'src/bar.dart';
20 | export 'src/interface.dart';
21 | export 'src/item.dart';
22 | export 'src/fab.dart';
23 |
--------------------------------------------------------------------------------
/lib/src/chip_builder.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/material.dart';
18 | import 'package:flutter/widgets.dart';
19 |
20 | import 'interface.dart';
21 |
22 | /// Simple badge with num inside.
23 | class DefaultChipBuilder extends ChipBuilder {
24 | /// key-value map, stands for the badge data.
25 | final Map chips;
26 |
27 | /// Color of badge text.
28 | final Color textColor;
29 |
30 | /// Color of the badge chip.
31 | final Color badgeColor;
32 |
33 | /// Padding for badge.
34 | final EdgeInsets padding;
35 |
36 | /// Margin for badge.
37 | final EdgeInsets margin;
38 |
39 | /// Radius corner for badge.
40 | final double borderRadius;
41 |
42 | /// Create a chip builder
43 | DefaultChipBuilder(
44 | this.chips, {
45 | required this.textColor,
46 | required this.badgeColor,
47 | required this.padding,
48 | required this.margin,
49 | required this.borderRadius,
50 | });
51 |
52 | @override
53 | Widget build(_, child, i, active) {
54 | var chip = chips[i];
55 | if (chip == null || chip == '') {
56 | return child;
57 | }
58 | return Stack(
59 | alignment: Alignment.center,
60 | children: [child, asBadge(chip)],
61 | );
62 | }
63 |
64 | /// Convert a chip data into [Widget].
65 | ///
66 | /// * [chip] String, return a [Text] badge;
67 | /// * [chip] IconData, return a [Icon] badge;
68 | /// * [chip] Widget, return a [Widget] badge;
69 | Widget asBadge(dynamic chip) {
70 | if (chip is String) {
71 | return Positioned.fill(
72 | child: Align(
73 | alignment: Alignment.center,
74 | child: Container(
75 | margin: margin,
76 | padding: padding,
77 | decoration: BoxDecoration(
78 | shape: BoxShape.rectangle,
79 | color: badgeColor,
80 | borderRadius: BorderRadius.circular(borderRadius),
81 | ),
82 | child: Text(chip, style: TextStyle(color: textColor, fontSize: 12)),
83 | ),
84 | ),
85 | );
86 | } else if (chip is IconData) {
87 | return Positioned.fill(
88 | child: Align(
89 | alignment: Alignment.center,
90 | child: Container(
91 | margin: margin,
92 | padding: padding,
93 | child: Icon(chip, color: badgeColor, size: 14),
94 | ),
95 | ),
96 | );
97 | } else if (chip is Widget) {
98 | return Positioned.fill(
99 | child: Align(
100 | alignment: Alignment.center,
101 | child: Container(margin: margin, padding: padding, child: chip),
102 | ),
103 | );
104 | } else if (chip is Color) {
105 | return Positioned.fill(
106 | child: Align(
107 | alignment: Alignment.center,
108 | child: Container(
109 | margin: margin,
110 | padding: padding,
111 | child: Container(
112 | decoration: BoxDecoration(shape: BoxShape.circle, color: chip),
113 | width: 10,
114 | height: 10,
115 | ),
116 | ),
117 | ),
118 | );
119 | } else {
120 | return Container();
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/lib/src/convex_shape.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | // Copyright 2014 The Flutter Authors. All rights reserved.
17 | // Use of this source code is governed by a BSD-style license that can be
18 | // found in the LICENSE file.
19 |
20 | import 'package:flutter/painting.dart';
21 | import 'dart:math' as math;
22 |
23 | /// A convex shape which implemented [NotchedShape].
24 | ///
25 | /// It's used to draw a convex shape for [ConvexAppBar], If you are interested about
26 | /// the math calculation, please refer to [CircularNotchedRectangle], it's based
27 | /// on Bezier curve;
28 | ///
29 | /// See also:
30 | ///
31 | /// * [CircularNotchedRectangle], a rectangle with a smooth circular notch.
32 | class ConvexNotchedRectangle extends NotchedShape {
33 | /// Draw the background with topLeft and topRight corner
34 | final double radius;
35 |
36 | /// Create Shape instance
37 | const ConvexNotchedRectangle({this.radius = 0});
38 |
39 | @override
40 | Path getOuterPath(Rect host, Rect? guest) {
41 | if (guest == null || !host.overlaps(guest)) return Path()..addRect(host);
42 |
43 | // The guest's shape is a circle bounded by the guest rectangle.
44 | // So the guest's radius is half the guest width.
45 | final notchRadius = guest.width / 2.0;
46 |
47 | const s1 = 15.0;
48 | const s2 = 1.0;
49 |
50 | final r = notchRadius;
51 | final a = -1.0 * r - s2;
52 | final b = host.top - guest.center.dy;
53 |
54 | final n2 = math.sqrt(b * b * r * r * (a * a + b * b - r * r));
55 | final p2xA = ((a * r * r) - n2) / (a * a + b * b);
56 | final p2xB = ((a * r * r) + n2) / (a * a + b * b);
57 | final p2yA = -math.sqrt(r * r - p2xA * p2xA);
58 | final p2yB = -math.sqrt(r * r - p2xB * p2xB);
59 |
60 | final p = List.filled(6, Offset.zero, growable: false);
61 | // p0, p1, and p2 are the control points for segment A.
62 | p[0] = Offset(a - s1, b);
63 | p[1] = Offset(a, b);
64 | final cmp = b < 0 ? -1.0 : 1.0;
65 | p[2] = cmp * p2yA > cmp * p2yB ? Offset(p2xA, p2yA) : Offset(p2xB, p2yB);
66 |
67 | // p3, p4, and p5 are the control points for segment B, which is a mirror
68 | // of segment A around the y axis.
69 | p[3] = Offset(-1.0 * p[2].dx, p[2].dy);
70 | p[4] = Offset(-1.0 * p[1].dx, p[1].dy);
71 | p[5] = Offset(-1.0 * p[0].dx, p[0].dy);
72 |
73 | // translate all points back to the absolute coordinate system.
74 | for (var i = 0; i < p.length; i += 1) {
75 | p[i] = p[i] + guest.center;
76 | //p[i] += padding;
77 | }
78 |
79 | return radius > 0
80 | ? (Path()
81 | ..moveTo(host.left, host.top + radius)
82 | ..arcToPoint(Offset(host.left + radius, host.top),
83 | radius: Radius.circular(radius))
84 | ..lineTo(p[0].dx, p[0].dy)
85 | ..quadraticBezierTo(p[1].dx, p[1].dy, p[2].dx, p[2].dy)
86 | ..arcToPoint(
87 | p[3],
88 | radius: Radius.circular(notchRadius),
89 | clockwise: true,
90 | )
91 | ..quadraticBezierTo(p[4].dx, p[4].dy, p[5].dx, p[5].dy)
92 | ..lineTo(host.right - radius, host.top)
93 | ..arcToPoint(Offset(host.right, host.top + radius),
94 | radius: Radius.circular(radius))
95 | ..lineTo(host.right, host.bottom)
96 | ..lineTo(host.left, host.bottom)
97 | ..close())
98 | : (Path()
99 | ..moveTo(host.left, host.top)
100 | ..lineTo(p[0].dx, p[0].dy)
101 | ..quadraticBezierTo(p[1].dx, p[1].dy, p[2].dx, p[2].dy)
102 | ..arcToPoint(
103 | p[3],
104 | radius: Radius.circular(notchRadius),
105 | clockwise: true,
106 | )
107 | ..quadraticBezierTo(p[4].dx, p[4].dy, p[5].dx, p[5].dy)
108 | ..lineTo(host.right, host.top)
109 | ..lineTo(host.right, host.bottom)
110 | ..lineTo(host.left, host.bottom)
111 | ..close());
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/lib/src/fab.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:convex_bottom_bar/src/painter.dart';
18 | import 'package:flutter/material.dart';
19 |
20 | /// Single convex button widget
21 | class ConvexButton extends StatelessWidget {
22 | static const _DEFAULT_SIZE = 60.0;
23 | static const _DEFAULT_TOP = 50.0;
24 | static const _DEFAULT_SIGMA = 2.0;
25 | static const _DEFAULT_THICKNESS = 4.0;
26 |
27 | /// Size of convex shape, should be lager than [top]
28 | final double? size;
29 |
30 | /// The distance to edge from the bottom of child widget.
31 | final double? top;
32 |
33 | /// Height of bottom border
34 | final double? thickness;
35 |
36 | /// Sigma for border
37 | final double? sigma;
38 |
39 | /// Optional child widget, default to be a widget of Icons.keyboard_voice
40 | final Widget child;
41 |
42 | /// Color for the button
43 | final Color? backgroundColor;
44 |
45 | /// Make new instance of [ConvexButton]
46 | const ConvexButton({
47 | Key? key,
48 | this.size,
49 | this.sigma,
50 | required this.child,
51 | this.thickness,
52 | this.backgroundColor,
53 | this.top,
54 | }) : super(key: key);
55 |
56 | /// Make a centered convex button.
57 | ///
58 | /// 
59 | factory ConvexButton.fab({
60 | Key? key,
61 | double? size,
62 | double? thickness,
63 | double? top,
64 | double? sigma,
65 | double iconSize = 32,
66 | double border = 2,
67 | Color color = Colors.redAccent,
68 | IconData icon = Icons.keyboard_voice,
69 | Color? backgroundColor,
70 | VoidCallback? onTap,
71 | }) {
72 | thickness = thickness ?? _DEFAULT_THICKNESS;
73 | var fab = Container(
74 | margin: EdgeInsets.only(bottom: thickness),
75 | decoration: BoxDecoration(
76 | shape: BoxShape.circle,
77 | border: Border.all(color: color, width: border),
78 | ),
79 | child: Icon(icon, color: color, size: iconSize),
80 | );
81 | return ConvexButton(
82 | key: key,
83 | size: size,
84 | thickness: thickness,
85 | top: top,
86 | backgroundColor: backgroundColor,
87 | sigma: sigma,
88 | child: GestureDetector(onTap: onTap, child: fab),
89 | );
90 | }
91 |
92 | @override
93 | Widget build(BuildContext context) {
94 | return Stack(alignment: Alignment.bottomCenter, children: [
95 | Container(
96 | height: thickness ?? _DEFAULT_THICKNESS,
97 | width: double.infinity,
98 | child: CustomPaint(
99 | painter: ConvexPainter(
100 | top: -(top ?? _DEFAULT_TOP),
101 | width: size ?? _DEFAULT_SIZE,
102 | height: size ?? _DEFAULT_SIZE,
103 | color: backgroundColor ?? Colors.grey[50]!,
104 | sigma: sigma ?? _DEFAULT_SIGMA,
105 | leftPercent: const AlwaysStoppedAnimation(0.5),
106 | ),
107 | ),
108 | ),
109 | child,
110 | ]);
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/lib/src/interface.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/widgets.dart';
18 |
19 | /// Tab callback, [index] are tab index which is being clicked.
20 | typedef GestureTapIndexCallback = void Function(int index);
21 |
22 | /// Fire before [GestureTapIndexCallback] is handled, you may return false to block the tap event.
23 | typedef TapNotifier = bool Function(int index);
24 |
25 | /// Tab builder.
26 | /// * [context] BuildContent instance
27 | /// * [index] index of tab
28 | /// * [active] active state for tab index
29 | typedef CustomTabBuilder = Widget Function(
30 | BuildContext context, int index, bool active);
31 |
32 | /// Interface to apply any custom badge chip.
33 | abstract class ChipBuilder {
34 | /// Construct a new widget which represent the tab item with custom badge.
35 | ///
36 | /// * [context] BuildContext instance;
37 | /// * [child] the tab item Widget;
38 | /// * [index] index of the tab item;
39 | /// * [active] active state for the index;
40 | Widget build(BuildContext context, Widget child, int index, bool active);
41 | }
42 |
43 | /// Item builder.
44 | abstract class DelegateBuilder {
45 | /// Called when the tab item is build.
46 | /// * [context] BuildContext instance;
47 | /// * [index] tab index;
48 | /// * [active] tab state;
49 | Widget build(BuildContext context, int index, bool active);
50 |
51 | /// Whether the convex shape is fixed center or positioned according to selection.
52 | bool fixed() {
53 | return false;
54 | }
55 | }
56 |
57 | /// Default tab styles are configured with internal layout/size, these are not
58 | /// exposed like color or height information. You can use [ConvexAppBar.builder]
59 | /// to fully customize the tab widget.
60 | ///
61 | /// However, if you just want to override some of the internal config and willing
62 | /// to take risk of the modified effects, try with the config carefully.
63 | abstract class StyleHook {
64 | /// size of icon
65 | double? get iconSize;
66 |
67 | /// margin outside of icon
68 | double get activeIconMargin;
69 |
70 | /// size of convex icon
71 | double get activeIconSize;
72 |
73 | /// style for text label.
74 | ///
75 | /// Warning:
76 | /// Override the text size can lead to `layout overflow` warning, you may need
77 | /// to update the height of bar too.
78 | TextStyle textStyle(Color color, String? fontFamily);
79 |
80 | /// For styles with both ICON and label, omit the Text widget when label is null/empty
81 | bool get hideEmptyLabel {
82 | return true;
83 | }
84 |
85 | /// layout size are relative to icon size and margin
86 | double get layoutSize {
87 | return activeIconMargin * 4 + activeIconSize;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/lib/src/item.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/cupertino.dart';
18 |
19 | /// Tab item used for [ConvexAppBar].
20 | class TabItem {
21 | /// this code is added by moein
22 | final String? fontFamily;
23 |
24 | /// Tab text.
25 | final String? title;
26 |
27 | /// IconData or Image.
28 | ///
29 | /// 
30 | final T icon;
31 |
32 | /// Optional if not provided ,[icon] is used.
33 | final T? activeIcon;
34 |
35 | /// Whether icon should blend with color.
36 | /// If [icon] is instance of [IconData] then blend is default to true, otherwise false
37 | final bool blend;
38 |
39 | /// Create item
40 | const TabItem({
41 | this.fontFamily,
42 | this.title = '',
43 | required this.icon,
44 | this.activeIcon,
45 | bool? isIconBlend,
46 | }) : assert(icon is IconData || icon is Widget,
47 | 'TabItem only support IconData and Widget'),
48 | blend = isIconBlend ?? (icon is IconData);
49 | }
50 |
--------------------------------------------------------------------------------
/lib/src/painter.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/material.dart';
18 |
19 | import 'convex_shape.dart';
20 | import 'reused_gradient.dart';
21 |
22 | /// Custom painter to draw the [ConvexNotchedRectangle] into canvas.
23 | class ConvexPainter extends CustomPainter {
24 | final _paint = Paint();
25 | final _shadowPaint = Paint();
26 | late ConvexNotchedRectangle _shape;
27 | final ReusedGradient _gradient = ReusedGradient();
28 |
29 | /// Width of the convex shape.
30 | final double width;
31 |
32 | /// Height of the convex shape.
33 | final double height;
34 |
35 | /// Position in vertical which describe the offset of shape.
36 | final double top;
37 |
38 | /// Position in horizontal which describe the offset of shape.
39 | final Animation leftPercent;
40 |
41 | /// RLT support
42 | final TextDirection? textDirection;
43 |
44 | /// Create painter
45 | ConvexPainter({
46 | required this.top,
47 | required this.width,
48 | required this.height,
49 | this.leftPercent = const AlwaysStoppedAnimation(0.5),
50 | this.textDirection,
51 | Color color = Colors.white,
52 | Color shadowColor = Colors.black38,
53 | double sigma = 2,
54 | Gradient? gradient,
55 | double? cornerRadius,
56 | }) : super(repaint: leftPercent) {
57 | _paint.color = color;
58 | try {
59 | _shadowPaint
60 | ..color = shadowColor
61 | ..maskFilter = MaskFilter.blur(BlurStyle.outer, sigma);
62 | } catch (e, s) {
63 | debugPrintStack(label: 'ElevationError', stackTrace: s);
64 | }
65 | _gradient.gradient = gradient;
66 | _shape = ConvexNotchedRectangle(radius: cornerRadius ?? 0);
67 | }
68 |
69 | @override
70 | void paint(Canvas canvas, Size size) {
71 | var host = Rect.fromLTWH(0, 0, size.width, size.height);
72 | var percent = textDirection == TextDirection.rtl
73 | ? (1 - leftPercent.value)
74 | : leftPercent.value;
75 | var guest =
76 | Rect.fromLTWH(size.width * percent - width / 2, top, width, height);
77 | _gradient.updateWith(_paint, size: host);
78 | var path = _shape.getOuterPath(host, guest);
79 | canvas.drawPath(path, _shadowPaint);
80 | canvas.drawPath(path, _paint);
81 | }
82 |
83 | @override
84 | bool shouldRepaint(ConvexPainter oldDelegate) {
85 | return oldDelegate.leftPercent.value != leftPercent.value ||
86 | oldDelegate._paint != _paint;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/lib/src/reused_gradient.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/painting.dart';
18 |
19 | /// Wrapper for [Gradient], we don't want to re-create instance frequently in
20 | /// hot method, such as paint().
21 | class ReusedGradient {
22 | Gradient? _gradient;
23 |
24 | Shader? _shader;
25 | Rect? _size;
26 |
27 | /// Create gradient
28 | ReusedGradient();
29 |
30 | /// Setter to reset the [Gradient] instance
31 | set gradient(Gradient? gradient) {
32 | _gradient = gradient;
33 | _size = null;
34 | }
35 |
36 | /// Update the paint with provided size
37 | void updateWith(Paint paint, {Rect? size}) {
38 | if (size == _size || size == null) {
39 | return;
40 | }
41 | if (_gradient == null) {
42 | return;
43 | }
44 | _shader ??= _gradient!.createShader(size);
45 | paint.shader = _shader;
46 | _size = size;
47 | }
48 |
49 | /// check if the gradient is valid or not
50 | bool get valid {
51 | return _size != null && _gradient != null;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lib/src/stack.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/rendering.dart';
18 | import 'package:flutter/widgets.dart' as widget;
19 |
20 | /// The `Stack` widget has limited hit test when child is overflow with
21 | /// `Positioned`. This behavior is intentional. For more detail refer to
22 | /// the bug report: [Document that widgets in the overflow of stack do not
23 | /// respond to gestures](https://github.com/flutter/flutter/issues/19445).
24 | ///
25 | /// The optional way to enable the hit test is define a new Stack and remove
26 | /// the size checking when the Stack instance is overflow enable.
27 | ///
28 | class Stack extends widget.Stack {
29 | /// Create stack instance
30 | Stack({
31 | widget.Key? key,
32 | AlignmentGeometry alignment = AlignmentDirectional.topStart,
33 | TextDirection? textDirection,
34 | StackFit fit = StackFit.loose,
35 | Clip clipBehavior = Clip.hardEdge,
36 | List children = const [],
37 | }) : super(
38 | key: key,
39 | alignment: alignment,
40 | textDirection: textDirection,
41 | fit: fit,
42 | clipBehavior: clipBehavior,
43 | children: children,
44 | );
45 |
46 | @override
47 | RenderStack createRenderObject(widget.BuildContext context) {
48 | return _RenderStack(
49 | alignment: alignment,
50 | textDirection: textDirection ?? widget.Directionality.of(context),
51 | fit: fit,
52 | clipBehavior: clipBehavior,
53 | );
54 | }
55 | }
56 |
57 | /// Enable overflow hitTest
58 | class _RenderStack extends RenderStack {
59 | _RenderStack({
60 | List? children,
61 | AlignmentGeometry alignment = AlignmentDirectional.topStart,
62 | TextDirection? textDirection,
63 | StackFit fit = StackFit.loose,
64 | Clip clipBehavior = Clip.hardEdge,
65 | }) : super(
66 | children: children,
67 | alignment: alignment,
68 | textDirection: textDirection,
69 | clipBehavior: clipBehavior,
70 | fit: fit,
71 | );
72 |
73 | @override
74 | bool hitTest(BoxHitTestResult result, {required Offset position}) {
75 | if (clipBehavior == Clip.none || size.contains(position)) {
76 | if (hitTestChildren(result, position: position) ||
77 | hitTestSelf(position)) {
78 | result.add(BoxHitTestEntry(this, position));
79 | return true;
80 | }
81 | }
82 | return false;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/lib/src/style/blend_image_icon.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/cupertino.dart';
18 | import 'package:flutter/foundation.dart';
19 | import 'package:flutter/material.dart';
20 |
21 | /// Decorate the provided [Image] or [IconData].
22 | class BlendImageIcon extends StatelessWidget {
23 | /// Create image widget
24 | const BlendImageIcon(this.image, {Key? key, this.color, this.size})
25 | : assert(image is Widget || image is IconData,
26 | 'image must be IconData or Widget'),
27 | super(key: key);
28 |
29 | /// Color used for Icon and gradient.
30 | final Color? color;
31 |
32 | /// Child image.
33 | final T image;
34 |
35 | /// Size of icon.
36 | final double? size;
37 |
38 | @override
39 | Widget build(BuildContext context) {
40 | var s = size ?? IconTheme.of(context).size;
41 | if (image is Widget) {
42 | // flutter web do not support shader mask. (flutter v1.12.x)
43 | var showRawImage = kIsWeb || color == null;
44 | if (showRawImage) {
45 | return SizedBox(
46 | width: s,
47 | height: s,
48 | child: image as Widget,
49 | );
50 | }
51 | return SizedBox(
52 | width: s,
53 | height: s,
54 | child: ShaderMask(
55 | shaderCallback: (Rect bounds) {
56 | return LinearGradient(colors: [color!, color!])
57 | .createShader(bounds);
58 | },
59 | blendMode: BlendMode.srcIn,
60 | child: image as Widget,
61 | ),
62 | );
63 | }
64 | return Icon(image as IconData, size: s, color: color);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/lib/src/style/fixed_circle_tab_style.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/material.dart';
18 |
19 | import '../item.dart';
20 | import 'blend_image_icon.dart';
21 | import 'inner_builder.dart';
22 |
23 | /// Convex shape is fixed center with circle.
24 | class FixedCircleTabStyle extends InnerBuilder {
25 | /// Color used as background of appbar and circle icon.
26 | final Color backgroundColor;
27 |
28 | /// Index of the centered convex shape.
29 | final int convexIndex;
30 |
31 | /// Create style builder
32 | FixedCircleTabStyle(
33 | {required List items,
34 | required Color activeColor,
35 | required Color color,
36 | required this.backgroundColor,
37 | required this.convexIndex})
38 | : super(items: items, activeColor: activeColor, color: color);
39 |
40 | @override
41 | Widget build(BuildContext context, int index, bool active) {
42 | var c = active ? activeColor : color;
43 | var item = items[index];
44 | var style = ofStyle(context);
45 | var textStyle = style.textStyle(c, item.fontFamily);
46 | var margin = style.activeIconMargin;
47 |
48 | if (index == convexIndex) {
49 | final item = items[index];
50 | return Container(
51 | // necessary otherwise the badge will not large enough
52 | width: style.layoutSize,
53 | height: style.layoutSize,
54 | decoration: BoxDecoration(
55 | shape: BoxShape.circle,
56 | color: c,
57 | ),
58 | margin: EdgeInsets.all(margin),
59 | child: BlendImageIcon(
60 | active ? item.activeIcon ?? item.icon : item.icon,
61 | size: style.activeIconSize,
62 | color: item.blend ? backgroundColor : null,
63 | ),
64 | );
65 | }
66 |
67 | var noLabel = style.hideEmptyLabel && hasNoText(item);
68 | var icon = BlendImageIcon(
69 | active ? item.activeIcon ?? item.icon : item.icon,
70 | color: item.blend ? (c) : null,
71 | size: style.iconSize,
72 | );
73 | var children = noLabel
74 | ? [icon]
75 | : [icon, Text(item.title ?? '', style: textStyle)];
76 | return Container(
77 | padding: EdgeInsets.only(bottom: 2),
78 | child: Column(
79 | mainAxisAlignment: MainAxisAlignment.center,
80 | children: children,
81 | ),
82 | );
83 | }
84 |
85 | @override
86 | bool fixed() {
87 | return true;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/lib/src/style/fixed_tab_style.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/material.dart';
18 |
19 | import '../item.dart';
20 | import 'blend_image_icon.dart';
21 | import 'inner_builder.dart';
22 |
23 | /// Convex shape is fixed center.
24 | class FixedTabStyle extends InnerBuilder {
25 | /// Index of the centered convex shape.
26 | final int convexIndex;
27 |
28 | /// Create style builder.
29 | FixedTabStyle({
30 | required List items,
31 | required Color activeColor,
32 | required Color color,
33 | required this.convexIndex,
34 | }) : super(items: items, activeColor: activeColor, color: color);
35 |
36 | @override
37 | Widget build(BuildContext context, int index, bool active) {
38 | var c = active ? activeColor : color;
39 | var style = ofStyle(context);
40 | var item = items[index];
41 | var textStyle = style.textStyle(c, item.fontFamily);
42 |
43 | if (index == convexIndex) {
44 | var item = items[convexIndex];
45 | return Container(
46 | padding: EdgeInsets.only(bottom: 2),
47 | child: Column(
48 | mainAxisAlignment: MainAxisAlignment.end,
49 | children: [
50 | BlendImageIcon(
51 | active ? item.activeIcon ?? item.icon : item.icon,
52 | color: item.blend ? (c) : null,
53 | size: style.activeIconSize,
54 | ),
55 | Text(item.title ?? '', style: textStyle)
56 | ],
57 | ),
58 | );
59 | }
60 |
61 | var noLabel = style.hideEmptyLabel && hasNoText(item);
62 | var icon = BlendImageIcon(
63 | active ? item.activeIcon ?? item.icon : item.icon,
64 | size: style.iconSize,
65 | color: item.blend ? (c) : null,
66 | );
67 | var children = noLabel
68 | ? [icon]
69 | : [icon, Text(item.title ?? '', style: textStyle)];
70 | return Container(
71 | padding: EdgeInsets.only(bottom: 2),
72 | child: Column(
73 | mainAxisAlignment: MainAxisAlignment.end,
74 | children: children,
75 | ),
76 | );
77 | }
78 |
79 | @override
80 | bool fixed() {
81 | return true;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/lib/src/style/flip_tab_style.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/material.dart';
18 |
19 | import '../item.dart';
20 | import 'blend_image_icon.dart';
21 | import 'inner_builder.dart';
22 | import 'transition_container.dart';
23 |
24 | /// Tab item are flipped when click.
25 | class FlipTabStyle extends InnerBuilder {
26 | /// Curve for flip transition.
27 | final Curve curve;
28 |
29 | /// Create style builder.
30 | FlipTabStyle({
31 | required List items,
32 | required Color activeColor,
33 | required Color color,
34 | required this.curve,
35 | }) : super(items: items, activeColor: activeColor, color: color);
36 |
37 | @override
38 | Widget build(BuildContext context, int index, bool active) {
39 | var item = items[index];
40 | var style = ofStyle(context);
41 | var textStyle = style.textStyle(activeColor, item.fontFamily);
42 |
43 | if (active) {
44 | var children = [
45 | BlendImageIcon(
46 | item.activeIcon ?? item.icon,
47 | color: item.blend ? activeColor : null,
48 | size: style.activeIconSize,
49 | ),
50 | ];
51 | var noLabel = style.hideEmptyLabel && hasNoText(item);
52 | if (!noLabel) {
53 | children.add(Text(item.title ?? '', style: textStyle));
54 | }
55 | return TransitionContainer.flip(
56 | data: index,
57 | duration: Duration(milliseconds: 500),
58 | height: style.activeIconMargin + style.activeIconSize,
59 | bottomChild: Container(
60 | padding: EdgeInsets.only(bottom: 2),
61 | child: Column(
62 | mainAxisAlignment: MainAxisAlignment.end,
63 | children: children,
64 | ),
65 | ),
66 | topChild: Container(
67 | child: Center(
68 | child: BlendImageIcon(
69 | item.icon,
70 | color: item.blend ? color : null,
71 | size: style.iconSize,
72 | ),
73 | ),
74 | ),
75 | curve: curve,
76 | );
77 | }
78 | return Center(
79 | child: BlendImageIcon(
80 | item.icon,
81 | color: item.blend ? color : null,
82 | size: style.iconSize,
83 | ),
84 | );
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/lib/src/style/inner_builder.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/cupertino.dart';
18 |
19 | import '../bar.dart';
20 | import '../interface.dart';
21 | import '../item.dart';
22 | import 'internal_style_config.dart';
23 |
24 | /// Simple builder which extend [DelegateBuilder] to provide some necessary config.
25 | abstract class InnerBuilder extends DelegateBuilder {
26 | /// List of [TabItem] stands for tabs.
27 | final List items;
28 |
29 | /// Color used when tab is active.
30 | final Color activeColor;
31 |
32 | /// Color used for tab.
33 | final Color color;
34 |
35 | /// Style hook to override the internal tab style
36 | StyleHook? _style;
37 |
38 | /// Create style builder.
39 | InnerBuilder(
40 | {required this.items, required this.activeColor, required this.color});
41 |
42 | /// Get style config
43 | StyleHook ofStyle(BuildContext context) {
44 | return StyleProvider.of(context)?.style ?? (_style ??= InternalStyle());
45 | }
46 |
47 | /// Return true if title text exists
48 | bool hasNoText(TabItem item) {
49 | return item.title == null || item.title!.isEmpty;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/lib/src/style/internal_style_config.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/material.dart';
18 |
19 | import '../../convex_bottom_bar.dart';
20 |
21 | /// Internal style configuration.
22 | class InternalStyle extends StyleHook {
23 | @override
24 | double? get iconSize {
25 | // use null will fallback to size of IconTheme
26 | return null;
27 | }
28 |
29 | @override
30 | double get activeIconMargin {
31 | return (ACTION_LAYOUT_SIZE - ACTION_INNER_BUTTON_SIZE) / 4;
32 | }
33 |
34 | @override
35 | double get activeIconSize {
36 | return ACTION_INNER_BUTTON_SIZE;
37 | }
38 |
39 | @override
40 | TextStyle textStyle(Color color, String? fontFamily) {
41 | return TextStyle(color: color, fontFamily: fontFamily);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/lib/src/style/react_circle_tab_style.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/material.dart';
18 |
19 | import '../../convex_bottom_bar.dart';
20 | import '../item.dart';
21 | import 'blend_image_icon.dart';
22 | import 'inner_builder.dart';
23 | import 'transition_container.dart';
24 |
25 | /// Convex shape is moved after selection.
26 | class ReactCircleTabStyle extends InnerBuilder {
27 | /// Color used as background of appbar and circle icon.
28 | final Color backgroundColor;
29 |
30 | /// Curve for tab transition.
31 | final Curve curve;
32 |
33 | /// Create style builder.
34 | ReactCircleTabStyle({
35 | required List items,
36 | required Color activeColor,
37 | required Color color,
38 | required this.backgroundColor,
39 | required this.curve,
40 | }) : super(items: items, activeColor: activeColor, color: color);
41 |
42 | @override
43 | Widget build(BuildContext context, int index, bool active) {
44 | var item = items[index];
45 | var style = ofStyle(context);
46 | var margin = style.activeIconMargin;
47 | if (active) {
48 | final item = items[index];
49 | return TransitionContainer.scale(
50 | data: index,
51 | curve: curve,
52 | child: Container(
53 | // necessary otherwise the badge will not large enough
54 | width: style.layoutSize,
55 | height: style.layoutSize,
56 | margin: EdgeInsets.all(margin),
57 | decoration: BoxDecoration(
58 | shape: BoxShape.circle,
59 | color: active ? activeColor : color,
60 | ),
61 | child: BlendImageIcon(
62 | active ? item.activeIcon ?? item.icon : item.icon,
63 | size: style.activeIconSize,
64 | color: item.blend ? backgroundColor : null,
65 | ),
66 | ),
67 | );
68 | }
69 | var textStyle = style.textStyle(color, item.fontFamily);
70 | var noLabel = style.hideEmptyLabel && hasNoText(item);
71 | var children = [
72 | BlendImageIcon(
73 | active ? item.activeIcon ?? item.icon : item.icon,
74 | size: style.iconSize,
75 | color: item.blend ? color : null,
76 | ),
77 | ];
78 | if (!noLabel) {
79 | children.add(Text(item.title ?? '', style: textStyle));
80 | }
81 | return Container(
82 | padding: EdgeInsets.only(bottom: 2),
83 | child: Column(
84 | mainAxisAlignment: MainAxisAlignment.center,
85 | children: children,
86 | ),
87 | );
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/lib/src/style/react_tab_style.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/material.dart';
18 |
19 | import '../item.dart';
20 | import 'blend_image_icon.dart';
21 | import 'inner_builder.dart';
22 | import 'transition_container.dart';
23 |
24 | /// Convex shape is moved after selection.
25 | class ReactTabStyle extends InnerBuilder {
26 | /// Curve for tab transition.
27 | final Curve curve;
28 |
29 | /// Create style builder.
30 | ReactTabStyle({
31 | required List items,
32 | required Color activeColor,
33 | required Color color,
34 | required this.curve,
35 | }) : super(items: items, activeColor: activeColor, color: color);
36 |
37 | @override
38 | Widget build(BuildContext context, int index, bool active) {
39 | var item = items[index];
40 | var style = ofStyle(context);
41 | var noLabel = style.hideEmptyLabel && hasNoText(item);
42 |
43 | if (active) {
44 | var children = [
45 | TransitionContainer.scale(
46 | data: index,
47 | curve: curve,
48 | child: BlendImageIcon(
49 | item.activeIcon ?? item.icon,
50 | color: item.blend ? activeColor : null,
51 | size: style.activeIconSize,
52 | ),
53 | ),
54 | ];
55 | if (!noLabel) {
56 | children.add(Text(item.title ?? '',
57 | style: style.textStyle(activeColor, item.fontFamily)));
58 | }
59 | return Container(
60 | padding: const EdgeInsets.only(bottom: 2),
61 | child: Column(
62 | mainAxisAlignment:
63 | noLabel ? MainAxisAlignment.center : MainAxisAlignment.end,
64 | children: children,
65 | ),
66 | );
67 | }
68 | var children = [
69 | BlendImageIcon(item.icon,
70 | color: item.blend ? color : null, size: style.iconSize),
71 | ];
72 | if (!noLabel) {
73 | children.add(Text(item.title ?? '',
74 | style: style.textStyle(color, item.fontFamily)));
75 | }
76 | return Container(
77 | padding: const EdgeInsets.only(bottom: 2),
78 | child: Column(
79 | mainAxisAlignment:
80 | noLabel ? MainAxisAlignment.center : MainAxisAlignment.end,
81 | children: children,
82 | ),
83 | );
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/lib/src/style/styles.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/cupertino.dart';
18 |
19 | import '../bar.dart';
20 | import '../interface.dart';
21 | import '../item.dart';
22 | import 'fixed_circle_tab_style.dart';
23 | import 'fixed_tab_style.dart';
24 | import 'flip_tab_style.dart';
25 | import 'react_circle_tab_style.dart';
26 | import 'react_tab_style.dart';
27 | import 'textin_tab_style.dart';
28 | import 'titled_tab_style.dart';
29 |
30 | /// Factory method to return the [DelegateBuilder] for each [TabStyle].
31 | DelegateBuilder supportedStyle(
32 | TabStyle style, {
33 | required List items,
34 | required Color color,
35 | required Color activeColor,
36 | required Color backgroundColor,
37 | required Curve curve,
38 | }) {
39 | assert(items.isNotEmpty, 'items should not be empty');
40 | assert(
41 | ((style == TabStyle.fixed || style == TabStyle.fixedCircle) &&
42 | items.length.isOdd) ||
43 | (style != TabStyle.fixed && style != TabStyle.fixedCircle),
44 | 'item count should be an odd number when using fixed/fixedCircle');
45 | DelegateBuilder builder;
46 | switch (style) {
47 | case TabStyle.fixed:
48 | builder = FixedTabStyle(
49 | items: items,
50 | color: color,
51 | activeColor: activeColor,
52 | convexIndex: items.length ~/ 2,
53 | );
54 | break;
55 | case TabStyle.fixedCircle:
56 | builder = FixedCircleTabStyle(
57 | items: items,
58 | color: color,
59 | activeColor: activeColor,
60 | backgroundColor: backgroundColor,
61 | convexIndex: items.length ~/ 2,
62 | );
63 | break;
64 | case TabStyle.react:
65 | builder = ReactTabStyle(
66 | items: items,
67 | color: color,
68 | activeColor: activeColor,
69 | curve: curve,
70 | );
71 | break;
72 | case TabStyle.reactCircle:
73 | builder = ReactCircleTabStyle(
74 | items: items,
75 | color: color,
76 | activeColor: activeColor,
77 | backgroundColor: backgroundColor,
78 | curve: curve,
79 | );
80 | break;
81 | case TabStyle.textIn:
82 | assert(items.every((it) => it.title != null && it.title!.isNotEmpty),
83 | 'title is necessary for TabStyle.textIn');
84 | builder = TextInTabStyle(
85 | items: items,
86 | color: color,
87 | activeColor: activeColor,
88 | curve: curve,
89 | );
90 | break;
91 | case TabStyle.titled:
92 | assert(items.every((it) => it.title != null && it.title!.isNotEmpty),
93 | 'title is necessary for TabStyle.titled');
94 | builder = TitledTabStyle(
95 | items: items,
96 | color: color,
97 | activeColor: activeColor,
98 | curve: curve,
99 | backgroundColor: backgroundColor,
100 | );
101 | break;
102 | case TabStyle.flip:
103 | builder = FlipTabStyle(
104 | items: items,
105 | color: color,
106 | activeColor: activeColor,
107 | curve: curve,
108 | );
109 | break;
110 | default:
111 | builder = ReactCircleTabStyle(
112 | items: items,
113 | color: color,
114 | activeColor: activeColor,
115 | backgroundColor: backgroundColor,
116 | curve: curve,
117 | );
118 | break;
119 | }
120 | return builder;
121 | }
122 |
--------------------------------------------------------------------------------
/lib/src/style/textin_tab_style.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/material.dart';
18 |
19 | import '../item.dart';
20 | import 'blend_image_icon.dart';
21 | import 'inner_builder.dart';
22 | import 'transition_container.dart';
23 |
24 | /// Tab icon, text animated with pop transition.
25 | class TextInTabStyle extends InnerBuilder {
26 | /// Curve for tab transition.
27 | final Curve curve;
28 |
29 | /// Create style builder.
30 | TextInTabStyle({
31 | required List items,
32 | required Color activeColor,
33 | required Color color,
34 | required this.curve,
35 | }) : super(items: items, activeColor: activeColor, color: color);
36 |
37 | @override
38 | Widget build(BuildContext context, int index, bool active) {
39 | var item = items[index];
40 | var style = ofStyle(context);
41 | if (active) {
42 | var textStyle = style.textStyle(activeColor, item.fontFamily);
43 | return Container(
44 | padding: const EdgeInsets.only(bottom: 2),
45 | child: Column(
46 | mainAxisAlignment: MainAxisAlignment.center,
47 | children: [
48 | TransitionContainer.scale(
49 | data: index,
50 | curve: curve,
51 | child: BlendImageIcon(
52 | item.activeIcon ?? item.icon,
53 | color: item.blend ? activeColor : null,
54 | size: style.activeIconSize,
55 | ),
56 | ),
57 | TransitionContainer.slide(
58 | curve: curve,
59 | child: Text(item.title ?? '', style: textStyle),
60 | ),
61 | ],
62 | ),
63 | );
64 | }
65 |
66 | return Center(
67 | child: BlendImageIcon(item.icon,
68 | size: style.iconSize, color: item.blend ? color : null),
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/lib/src/style/titled_tab_style.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/material.dart';
18 |
19 | import '../item.dart';
20 | import 'blend_image_icon.dart';
21 | import 'inner_builder.dart';
22 | import 'transition_container.dart';
23 |
24 | /// Tab icon, text animated with pop transition.
25 | class TitledTabStyle extends InnerBuilder {
26 | /// Curve for tab transition.
27 | final Curve curve;
28 |
29 | /// Color used as background of appbar and circle icon.
30 | final Color backgroundColor;
31 |
32 | /// Previous active tab index.
33 | int _preActivate = -1;
34 |
35 | /// Create style builder.
36 | TitledTabStyle({
37 | required List items,
38 | required Color activeColor,
39 | required Color color,
40 | required this.curve,
41 | required this.backgroundColor,
42 | }) : super(items: items, activeColor: activeColor, color: color);
43 |
44 | @override
45 | Widget build(BuildContext context, int index, bool active) {
46 | var pre = _preActivate;
47 | if (active) {
48 | _preActivate = index;
49 | }
50 | var item = items[index];
51 | var style = ofStyle(context);
52 | var margin = style.activeIconMargin;
53 |
54 | if (active) {
55 | return TransitionContainer.slide(
56 | data: index,
57 | duration: Duration(milliseconds: 200),
58 | curve: curve,
59 | child: Container(
60 | // necessary otherwise the badge will not large enough
61 | width: style.layoutSize,
62 | height: style.layoutSize,
63 | margin: EdgeInsets.all(margin),
64 | decoration: BoxDecoration(shape: BoxShape.circle, color: activeColor),
65 | child: BlendImageIcon(
66 | item.activeIcon ?? item.icon,
67 | size: style.activeIconSize,
68 | color: item.blend ? backgroundColor : null,
69 | ),
70 | ),
71 | );
72 | }
73 |
74 | var textStyle = style.textStyle(activeColor, item.fontFamily);
75 | if (pre == index) {
76 | return Stack(
77 | clipBehavior: Clip.hardEdge,
78 | alignment: Alignment.center,
79 | children: [
80 | Text(item.title ?? '', style: textStyle),
81 | TransitionContainer.slide(
82 | reverse: true,
83 | curve: curve,
84 | child: Container(
85 | margin: EdgeInsets.all(margin),
86 | decoration: BoxDecoration(
87 | shape: BoxShape.circle,
88 | color: activeColor,
89 | ),
90 | child: BlendImageIcon(
91 | item.activeIcon ?? item.icon,
92 | size: style.activeIconSize,
93 | color: item.blend ? backgroundColor : null,
94 | ),
95 | ),
96 | )
97 | ],
98 | );
99 | }
100 | return Center(child: Text(item.title ?? '', style: textStyle));
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/lib/src/style/transition_container.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:flutter/material.dart';
18 |
19 | import 'transition_container_builder.dart';
20 |
21 | /// Add controller with provided transition api, such as [SlideTransition], [ScaleTransition].
22 | class TransitionContainer extends StatefulWidget {
23 | /// Build transition.
24 | final TransitionContainerBuilder builder;
25 |
26 | /// Transition duration.
27 | final Duration? duration;
28 |
29 | /// Control whether the animation should be skipped when widget change.
30 | final int? data;
31 |
32 | /// Wrap a widget with scale transition.
33 | TransitionContainer.scale({
34 | required Widget child,
35 | required Curve curve,
36 | this.duration,
37 | this.data,
38 | }) : builder = ScaleBuilder(curve: curve, child: child);
39 |
40 | /// Wrap a widget with slide transition.
41 | TransitionContainer.slide({
42 | required Widget child,
43 | required Curve curve,
44 | this.duration,
45 | bool reverse = false,
46 | this.data,
47 | }) : builder = SlideBuilder(curve: curve, child: child, reverse: reverse);
48 |
49 | /// Wrap a widget with flip transition.
50 | TransitionContainer.flip({
51 | required Widget topChild,
52 | required Widget bottomChild,
53 | required Curve curve,
54 | required double height,
55 | this.duration,
56 | this.data,
57 | }) : builder = FlipBuilder(
58 | height,
59 | curve: curve,
60 | topChild: topChild,
61 | bottomChild: bottomChild,
62 | );
63 |
64 | @override
65 | _State createState() {
66 | return _State();
67 | }
68 | }
69 |
70 | class _State extends State with TickerProviderStateMixin {
71 | AnimationController? animationController;
72 | late Animation animation;
73 |
74 | @override
75 | void initState() {
76 | super.initState();
77 | _setAnimation();
78 | }
79 |
80 | void _setAnimation() {
81 | final controller = AnimationController(
82 | vsync: this,
83 | duration: widget.duration ?? Duration(milliseconds: 150),
84 | )..addListener(() => setState(() {}));
85 | controller.forward();
86 | animation = widget.builder.animation(controller);
87 | animationController = controller;
88 | }
89 |
90 | @override
91 | void didUpdateWidget(TransitionContainer oldWidget) {
92 | super.didUpdateWidget(oldWidget);
93 | if (oldWidget.builder.runtimeType != widget.builder.runtimeType) {
94 | animationController?.dispose();
95 | _setAnimation();
96 | } else {
97 | if (widget.data == oldWidget.data) {
98 | return;
99 | }
100 | animationController?.reset();
101 | animationController?.forward();
102 | }
103 | }
104 |
105 | @override
106 | void dispose() {
107 | animationController?.dispose();
108 | super.dispose();
109 | }
110 |
111 | @override
112 | Widget build(BuildContext context) {
113 | return widget.builder.build(animation);
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/lib/src/style/transition_container_builder.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'dart:math';
18 |
19 | import 'package:flutter/cupertino.dart';
20 |
21 | import 'transition_container.dart';
22 |
23 | /// Interface to provide a transition, work with [TransitionContainer].
24 | abstract class TransitionContainerBuilder {
25 | /// Curve for animation.
26 | final Curve curve;
27 |
28 | /// Create style builder.
29 | TransitionContainerBuilder(this.curve);
30 |
31 | /// Animation used for widget.
32 | Animation animation(AnimationController controller);
33 |
34 | /// Return animated widget with provided animation.
35 | Widget build(Animation animation);
36 | }
37 |
38 | /// Scale transition builder.
39 | class ScaleBuilder extends TransitionContainerBuilder {
40 | /// The target widget to scale with.
41 | Widget child;
42 |
43 | @override
44 | Animation animation(AnimationController controller) {
45 | return CurvedAnimation(parent: controller, curve: curve);
46 | }
47 |
48 | @override
49 | Widget build(Animation animation) {
50 | return ScaleTransition(scale: animation, child: child);
51 | }
52 |
53 | /// Create scale builder
54 | ScaleBuilder({required Curve curve, required this.child}) : super(curve);
55 | }
56 |
57 | /// Slide transition builder.
58 | class SlideBuilder extends TransitionContainerBuilder {
59 | /// The target widget to slide with.
60 | Widget child;
61 |
62 | /// slide direction.
63 | final bool reverse;
64 |
65 | /// Create slide builder.
66 | SlideBuilder(
67 | {required Curve curve, required this.child, required this.reverse})
68 | : super(curve);
69 |
70 | @override
71 | Widget build(Animation animation) {
72 | return SlideTransition(position: animation, child: child);
73 | }
74 |
75 | @override
76 | Animation animation(AnimationController controller) {
77 | return Tween(
78 | begin: reverse ? Offset.zero : const Offset(0.0, 2.0),
79 | end: reverse ? const Offset(0.0, 2.0) : Offset.zero,
80 | ).animate(CurvedAnimation(parent: controller, curve: curve));
81 | }
82 | }
83 |
84 | /// This flip animation is origin from [https://github.com/deven98/flip_box_bar/blob/master/lib/src/flip_box.dart]
85 | /// UX => .
86 | class FlipBuilder extends TransitionContainerBuilder {
87 | /// Top widget.
88 | final Widget topChild;
89 |
90 | /// Bottom widget.
91 | final Widget bottomChild;
92 |
93 | /// Size of builder.
94 | final double height;
95 |
96 | /// Create flip builder
97 | FlipBuilder(
98 | this.height, {
99 | required Curve curve,
100 | required this.topChild,
101 | required this.bottomChild,
102 | }) : super(curve);
103 |
104 | @override
105 | Animation animation(AnimationController controller) {
106 | return Tween(begin: 0.0, end: pi / 2).animate(
107 | CurvedAnimation(parent: controller, curve: curve),
108 | );
109 | }
110 |
111 | @override
112 | Widget build(Animation animation) {
113 | return Container(
114 | child: Stack(
115 | children: [
116 | Transform(
117 | alignment: Alignment.bottomCenter,
118 | transform: Matrix4.identity()
119 | ..setEntry(3, 2, 0.001)
120 | ..translate(0.0, (cos(animation.value) * (height / 2)),
121 | ((height / 2) * sin(animation.value)))
122 | ..rotateX(-(pi / 2) + animation.value),
123 | child: Container(
124 | child: Center(child: bottomChild),
125 | ),
126 | ),
127 | animation.value < (85 * pi / 180)
128 | ? Transform(
129 | alignment: Alignment.bottomCenter,
130 | transform: Matrix4.identity()
131 | ..setEntry(3, 2, 0.001)
132 | ..translate(
133 | 0.0,
134 | -(height / 2) * sin(animation.value),
135 | ((height / 2) * cos(animation.value)),
136 | )
137 | ..rotateX(animation.value),
138 | child: Container(
139 | alignment: Alignment.bottomCenter,
140 | child: Center(child: topChild),
141 | ),
142 | )
143 | : Container(),
144 | ],
145 | ),
146 | );
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: convex_bottom_bar
2 | description: A Flutter package which implements a ConvexAppBar to show a convex tab in the bottom bar. Theming supported.
3 | version: 3.2.0
4 | homepage: https://github.com/hacktons/convex_bottom_bar
5 |
6 | environment:
7 | sdk: ">=2.12.0 <3.0.0"
8 |
9 | dependencies:
10 | flutter:
11 | sdk: flutter
12 |
13 | dev_dependencies:
14 | flutter_test:
15 | sdk: flutter
16 | pedantic: ^1.10.0-nullsafety.3
17 |
18 | # For information on the generic Dart part of this file, see the
19 | # following page: https://dart.dev/tools/pub/pubspec
20 |
21 | # The following section is specific to Flutter.
22 | flutter:
23 |
24 | # To add assets to your package, add an assets section, like this:
25 | # assets:
26 | # - images/a_dot_burr.jpeg
27 | # - images/a_dot_ham.jpeg
28 | #
29 | # For details regarding assets in packages, see
30 | # https://flutter.dev/assets-and-images/#from-packages
31 | #
32 | # An image asset can refer to one or more resolution-specific "variants", see
33 | # https://flutter.dev/assets-and-images/#resolution-aware.
34 |
35 | # To add custom fonts to your package, add a fonts section here,
36 | # in this "flutter" section. Each entry in this list should have a
37 | # "family" key with the font family name, and a "fonts" key with a
38 | # list giving the asset and other descriptors for the font. For
39 | # example:
40 | # fonts:
41 | # - family: Schyler
42 | # fonts:
43 | # - asset: fonts/Schyler-Regular.ttf
44 | # - asset: fonts/Schyler-Italic.ttf
45 | # style: italic
46 | # - family: Trajan Pro
47 | # fonts:
48 | # - asset: fonts/TrajanPro.ttf
49 | # - asset: fonts/TrajanPro_Bold.ttf
50 | # weight: 700
51 | #
52 | # For details regarding fonts in packages, see
53 | # https://flutter.dev/custom-fonts/#from-packages
54 |
--------------------------------------------------------------------------------
/test/provider_test.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:convex_bottom_bar/convex_bottom_bar.dart';
18 | import 'package:flutter/material.dart';
19 | import 'package:flutter_test/flutter_test.dart';
20 |
21 | import 'widget_test.dart';
22 |
23 | void main() {
24 | testWidgets('Test StyleProvider', (WidgetTester tester) async {
25 | await tester.pumpWidget(material(StyleProvider(
26 | style: Style(),
27 | child: Directionality(
28 | textDirection: TextDirection.rtl,
29 | child: ConvexAppBar(
30 | items: [
31 | TabItem(icon: Icons.gradient, title: ''),
32 | TabItem(icon: Icons.help_outline, title: ''),
33 | TabItem(icon: Icons.work, title: ''),
34 | ],
35 | ),
36 | ))));
37 | });
38 | // no longer needed for null safety
39 | /*testWidgets('Test Provider assertion, style should not be null',
40 | (WidgetTester tester) async {
41 | expect(() async {
42 | await tester.pumpWidget(StyleProvider(
43 | style: null,
44 | child: null,
45 | ));
46 | }, throwsAssertionError);
47 | });*/
48 | /*testWidgets('Test Provider assertion, child should not be null',
49 | (WidgetTester tester) async {
50 | expect(() async {
51 | await tester.pumpWidget(StyleProvider(
52 | style: Style(),
53 | child: null,
54 | ));
55 | }, throwsAssertionError);
56 | });*/
57 | }
58 |
59 | /// testWidgets('MyWidget asserts invalid bounds', (WidgetTester tester) async {
60 | /// await tester.pumpWidget(MyWidget(-1));
61 | /// expect(tester.takeException(), isAssertionError); // or isNull, as appropriate.
62 | /// });
63 | class Style extends StyleHook {
64 | @override
65 | double get iconSize {
66 | return 10;
67 | }
68 |
69 | @override
70 | TextStyle textStyle(Color color, String? fontFamily) {
71 | return TextStyle(color: color, fontFamily: fontFamily);
72 | }
73 |
74 | @override
75 | double get activeIconSize {
76 | return 10;
77 | }
78 |
79 | @override
80 | double get activeIconMargin {
81 | return 1;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/test/utils_test.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Chaobin Wu
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import 'package:convex_bottom_bar/src/reused_gradient.dart';
18 | import 'package:flutter/material.dart';
19 | import 'package:flutter_test/flutter_test.dart';
20 |
21 | void main() {
22 | test('Test Gradient', () {
23 | var gradient = ReusedGradient();
24 | gradient.gradient = LinearGradient(
25 | begin: Alignment.topLeft,
26 | end: Alignment.bottomRight,
27 | colors: [Colors.blue, Colors.redAccent, Colors.green, Colors.blue],
28 | tileMode: TileMode.repeated,
29 | );
30 | gradient.updateWith(Paint(), size: Rect.fromLTRB(0, 0, 10, 10));
31 | expect(gradient.valid, true);
32 | });
33 | }
34 |
--------------------------------------------------------------------------------