Skip to content
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

Shimmer effect #230

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions lib/screens/home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import '/widgets/stops_layer/stop_area_layer.dart';
import '/widgets/osm_element_layer/osm_element_layer.dart';
import '/widgets/question_dialog/question_dialog.dart';
import '/widgets/map_overlay/map_overlay.dart';
import '/widgets/map_overlay/shimmer.dart';
import '/widgets/home_sidebar/home_sidebar.dart';

class HomeScreen extends View<HomeViewModel> with PromptHandler {
Expand Down Expand Up @@ -141,6 +142,17 @@ class HomeScreen extends View<HomeViewModel> with PromptHandler {
);
},
),
IgnorePointer(
ignoring: true,
child: Shimmer(
isLoading: viewModel.isLoadingStopAreas,
child: Container(
color: Colors.transparent,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
),
),
),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to directly include the ignore pointer in the Shimmer widget.

Also please move this to the map_overlay.dart file making sure it is below the other overlay widgets like buttons and credits text.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left it in the home.dart because in the map_overlay.dart the shimmer does not cover the complete screen from left to right since it will be painted with a kind of border

RepaintBoundary(
child: AnimatedSwitcher(
switchInCurve: Curves.ease,
Expand Down
75 changes: 75 additions & 0 deletions lib/widgets/map_overlay/shimmer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import 'package:flutter/material.dart';

class _SlidingGradientTransform extends GradientTransform {
const _SlidingGradientTransform({
required this.slidePercent,
});

final double slidePercent;
yulieth9109 marked this conversation as resolved.
Show resolved Hide resolved

@override
Matrix4? transform(Rect bounds, {TextDirection? textDirection}) {
return Matrix4.translationValues(bounds.width * slidePercent, 0.0, 0.0);
}
}
yulieth9109 marked this conversation as resolved.
Show resolved Hide resolved

class Shimmer extends StatefulWidget {
final bool isLoading;
yulieth9109 marked this conversation as resolved.
Show resolved Hide resolved
final Widget child;

const Shimmer({
required this.isLoading,
required this.child,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can supply a SizedBox.expand as the default value and therefore drop the necessity of passing

                      child: Container(
                        color: Colors.transparent,
                        width: MediaQuery.of(context).size.width, 
                        height: MediaQuery.of(context).size.height,
                      ),

Perhaps alternatively you can even make it nullable (and basically have null as default value) since ShaderMask doesn't require a child. You just need to make sure to use a Positioned.fill in the stack later. (this would be my preferred solution)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried both ways and the shimmer was not painting in any way, then I still left the container.

Key? key,
}) : super(key: key);
yulieth9109 marked this conversation as resolved.
Show resolved Hide resolved

@override
_ShimmerState createState() => _ShimmerState();
yulieth9109 marked this conversation as resolved.
Show resolved Hide resolved
}

class _ShimmerState extends State<Shimmer>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
yulieth9109 marked this conversation as resolved.
Show resolved Hide resolved

@override
void initState() {
super.initState();
_controller = AnimationController.unbounded(vsync: this)
..repeat(min: -0.5, max: 1.5, period: const Duration(milliseconds: 1000));
yulieth9109 marked this conversation as resolved.
Show resolved Hide resolved
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}

LinearGradient get gradient => LinearGradient(
colors: [Colors.transparent, Theme.of(context).colorScheme.primary.withOpacity(0.7) , Colors.transparent,],
yulieth9109 marked this conversation as resolved.
Show resolved Hide resolved
stops: const [ 0.1, 0.3, 0.4,],
begin: const Alignment(-1.0, -0.3),
end: const Alignment(1.0, 0.3),
transform:
_SlidingGradientTransform(slidePercent: _controller.value),
);

@override
Widget build(BuildContext context) {
if (widget.isLoading) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return ShaderMask(
blendMode: BlendMode.color,
yulieth9109 marked this conversation as resolved.
Show resolved Hide resolved
shaderCallback: (Rect bounds) {
return gradient.createShader(bounds);
},
yulieth9109 marked this conversation as resolved.
Show resolved Hide resolved
child: widget.child,
);
},
);
} else {
return const SizedBox();
}
yulieth9109 marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading