-
Notifications
You must be signed in to change notification settings - Fork 595
Shimmer control
#5828
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Shimmer control
#5828
Changes from all commits
c14a285
972816b
c1e4ddf
04675d1
2ff5b31
7fc630a
4fdfe38
38023fb
8a24d42
cd6572c
8de68c4
fa401a9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| import 'package:collection/collection.dart'; | ||
| import 'package:flutter/material.dart'; | ||
| import 'package:shimmer/shimmer.dart'; | ||
|
|
||
| import '../extensions/control.dart'; | ||
| import '../models/control.dart'; | ||
| import '../utils/colors.dart'; | ||
| import '../utils/gradient.dart'; | ||
| import '../utils/numbers.dart'; | ||
| import '../utils/time.dart'; | ||
| import '../widgets/error.dart'; | ||
| import 'base_controls.dart'; | ||
|
|
||
| class ShimmerControl extends StatelessWidget { | ||
| final Control control; | ||
|
|
||
| const ShimmerControl({super.key, required this.control}); | ||
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
| debugPrint("Shimmer build: ${control.id}"); | ||
|
|
||
| final content = control.buildWidget("content"); | ||
| if (content == null) { | ||
| return const ErrorControl("Shimmer.content must be specified"); | ||
| } | ||
|
|
||
| final gradient = control.getGradient("gradient", Theme.of(context)); | ||
| final baseColor = control.getColor("base_color", context); | ||
| final highlightColor = control.getColor("highlight_color", context); | ||
|
|
||
| if (gradient == null && (baseColor == null || highlightColor == null)) { | ||
| return const ErrorControl( | ||
| "Shimmer requires either gradient or base/highlight colors"); | ||
| } | ||
|
|
||
| final direction = _parseDirection(control.getString("direction")); | ||
| final period = | ||
| control.getDuration("period", const Duration(milliseconds: 1500))!; | ||
| final loop = control.getInt("loop", 0)!; | ||
|
|
||
| final shimmerWidget = gradient != null | ||
| ? Shimmer( | ||
| gradient: gradient, | ||
| direction: direction, | ||
| period: period, | ||
| loop: loop, | ||
| enabled: !control.disabled, | ||
| child: content, | ||
| ) | ||
| : Shimmer.fromColors( | ||
| baseColor: baseColor!, | ||
| highlightColor: highlightColor!, | ||
| direction: direction, | ||
| period: period, | ||
| loop: loop, | ||
| enabled: !control.disabled, | ||
| child: content, | ||
| ); | ||
|
|
||
| return LayoutControl(control: control, child: shimmerWidget); | ||
| } | ||
| } | ||
|
|
||
| ShimmerDirection _parseDirection(String? value, | ||
| [ShimmerDirection defaultValue = ShimmerDirection.ltr]) { | ||
| if (value == null) { | ||
| return defaultValue; | ||
| } | ||
| return ShimmerDirection.values.firstWhereOrNull( | ||
| (dir) => dir.name.toLowerCase() == value.toLowerCase()) ?? | ||
| defaultValue; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,7 @@ | |
|
|
||
|
|
||
| def main(page: ft.Page): | ||
| page.overlay.append(hf := ft.HapticFeedback()) | ||
| hf = ft.HapticFeedback() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please document Also, the description/docstring of our custom services should explain or point (through a link) to the approach to be used, explained in the base Service. |
||
|
|
||
| async def heavy_impact(): | ||
| await hf.heavy_impact() | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,15 +2,13 @@ | |
|
|
||
|
|
||
| def main(page: ft.Page): | ||
| # just need hold a reference to ShakeDetector in the session store | ||
| page.session.store.set( | ||
| "shake_detector", | ||
| page.services.append( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice. Will this work for
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, similar to edit: ah, it wont be easily possible, since |
||
| ft.ShakeDetector( | ||
| minimum_shake_count=2, | ||
| shake_slop_time_ms=300, | ||
| shake_count_reset_time_ms=1000, | ||
| on_shake=lambda _: page.add(ft.Text("Shake detected!")), | ||
| ), | ||
| ) | ||
| ) | ||
|
|
||
| page.add(ft.Text("Shake your device!")) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| import flet as ft | ||
|
|
||
|
|
||
| def main(page: ft.Page): | ||
| page.add( | ||
| ft.Shimmer( | ||
| base_color=ft.Colors.with_opacity(0.3, ft.Colors.GREY_400), | ||
| highlight_color=ft.Colors.WHITE, | ||
| content=ft.Column( | ||
| controls=[ | ||
| ft.Container(height=80, bgcolor=ft.Colors.GREY_300), | ||
| ft.Container(height=80, bgcolor=ft.Colors.GREY_300), | ||
| ft.Container(height=80, bgcolor=ft.Colors.GREY_300), | ||
| ], | ||
| ), | ||
| ) | ||
| ) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| ft.run(main) |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add integration test for this example? |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| import flet as ft | ||
|
|
||
|
|
||
| def _line(width: int, height: int = 12) -> ft.Control: | ||
| return ft.Container( | ||
| width=width, | ||
| height=height, | ||
| bgcolor=ft.Colors.GREY_400, | ||
| border_radius=ft.BorderRadius.all(height), | ||
| ) | ||
|
|
||
|
|
||
| def _placeholder_tile() -> ft.Control: | ||
| return ft.Container( | ||
| padding=ft.Padding.all(16), | ||
| bgcolor=ft.Colors.with_opacity(0.3, ft.Colors.WHITE), | ||
| border_radius=ft.BorderRadius.all(20), | ||
| content=ft.Row( | ||
| spacing=16, | ||
| vertical_alignment=ft.CrossAxisAlignment.START, | ||
| controls=[ | ||
| ft.Container( | ||
| width=48, | ||
| height=48, | ||
| bgcolor=ft.Colors.with_opacity(0.5, ft.Colors.GREY_400), | ||
| border_radius=ft.BorderRadius.all(24), | ||
| content=ft.Icon(ft.Icons.PERSON, color=ft.Colors.GREY_500), | ||
| ), | ||
| ft.Column( | ||
| expand=True, | ||
| spacing=10, | ||
| controls=[ | ||
| _line(160), | ||
| _line(120), | ||
| ft.Row( | ||
| spacing=10, | ||
| vertical_alignment=ft.CrossAxisAlignment.CENTER, | ||
| controls=[_line(70, 10), _line(90, 10)], | ||
| ), | ||
| ], | ||
| ), | ||
| ft.Container( | ||
| width=32, | ||
| height=32, | ||
| bgcolor=ft.Colors.GREY_200, | ||
| border_radius=ft.BorderRadius.all(16), | ||
| ), | ||
| ], | ||
| ), | ||
| ) | ||
|
|
||
|
|
||
| def main(page: ft.Page): | ||
| page.title = "Shimmer - loading placeholders" | ||
|
|
||
| page.add( | ||
| ft.Shimmer( | ||
| base_color=ft.Colors.with_opacity(0.3, ft.Colors.GREY_400), | ||
| highlight_color=ft.Colors.WHITE, | ||
| content=ft.Column( | ||
| controls=[_placeholder_tile() for _ in range(3)], | ||
| ), | ||
| ), | ||
| ) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| ft.run(main) |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add integration test for this example? |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import flet as ft | ||
|
|
||
|
|
||
| def _stat_block(title: str, subtitle: str) -> ft.Control: | ||
| def metric(width: int, height: int = 14) -> ft.Control: | ||
| return ft.Container( | ||
| width=width, | ||
| height=height, | ||
| bgcolor=ft.Colors.WHITE, | ||
| opacity=0.6, | ||
| border_radius=ft.BorderRadius.all(height), | ||
| ) | ||
|
|
||
| return ft.Container( | ||
| width=200, | ||
| padding=ft.Padding.all(20), | ||
| bgcolor=ft.Colors.with_opacity(0.1, ft.Colors.BLACK), | ||
| border_radius=ft.BorderRadius.all(24), | ||
| content=ft.Column( | ||
| spacing=16, | ||
| controls=[ | ||
| metric(140), | ||
| ft.Row(spacing=10, controls=[metric(60, 10), metric(90, 10)]), | ||
| ft.Container( | ||
| border_radius=ft.BorderRadius.all(16), | ||
| bgcolor=ft.Colors.WHITE, | ||
| opacity=0.35, | ||
| ), | ||
| ft.Column(spacing=8, controls=[metric(120, 12), metric(160, 12)]), | ||
| ft.Text(title, weight=ft.FontWeight.W_600), | ||
| ft.Text(subtitle, size=12), | ||
| ], | ||
| ), | ||
| ) | ||
|
|
||
|
|
||
| def main(page: ft.Page): | ||
| page.title = "Shimmer - custom gradients" | ||
| page.bgcolor = "#0e0e18" | ||
| accent = ft.LinearGradient( | ||
| begin=ft.Alignment(-1.0, -0.5), | ||
| end=ft.Alignment(1.0, 0.5), | ||
| colors=[ | ||
| ft.Colors.PURPLE, | ||
| ft.Colors.PURPLE, | ||
| ft.Colors.AMBER_200, | ||
| ft.Colors.PURPLE, | ||
| ft.Colors.PURPLE, | ||
| ], | ||
| stops=[0.0, 0.35, 0.5, 0.65, 1.0], | ||
| ) | ||
|
|
||
| cards = ft.Row( | ||
| wrap=True, | ||
| controls=[ | ||
| ft.Shimmer( | ||
| gradient=accent, | ||
| direction=ft.ShimmerDirection.TTB, | ||
| period=2200, | ||
| content=_stat_block("Recent activity", "Smooth top-to-bottom sweep"), | ||
| ), | ||
| ], | ||
| ) | ||
|
|
||
| page.add(cards) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| ft.run(main) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| --- | ||
| class_name: flet.Shimmer | ||
| examples: ../../examples/controls/shimmer | ||
| example_images: ../test-images/examples/core/golden/macos/shimmer | ||
| --- | ||
|
|
||
| {{ class_summary(class_name, example_images + "/image_for_docs.gif", image_caption="Basic shimmer") }} | ||
|
|
||
| ## Examples | ||
|
|
||
| ### Basic | ||
|
|
||
| ```python | ||
| --8<-- "{{ examples }}/basic.py" | ||
| ``` | ||
|
|
||
| {{ image(example_images + "/image_for_docs.gif", alt="custom-label", width="50%") }} | ||
|
|
||
| ### Skeleton list placeholders | ||
|
|
||
| ```python | ||
| --8<-- "{{ examples }}/basic_placeholder.py" | ||
| ``` | ||
|
|
||
| {{ image(example_images + "/basic_placeholder.png", alt="custom-label", width="50%") }} | ||
|
|
||
| ### Custom gradients and directions | ||
|
|
||
| ```python | ||
| --8<-- "{{ examples }}/custom_gradient.py" | ||
| ``` | ||
|
|
||
| {{ image(example_images + "/custom_gradient.png", alt="custom-label", width="50%") }} | ||
|
|
||
| {{ class_members(class_name) }} |
Uh oh!
There was an error while loading. Please reload this page.