Skip to content

Commit

Permalink
Merge pull request #241 from Team-Weather/228-screen-loading-speed-im…
Browse files Browse the repository at this point in the history
…provement

228 screen loading speed improvement
  • Loading branch information
KimDonghyeok authored Jun 12, 2024
2 parents 02b637e + 78e5788 commit be6644f
Show file tree
Hide file tree
Showing 4 changed files with 256 additions and 48 deletions.
229 changes: 212 additions & 17 deletions lib/presentation/home/component/recommend_ootd_list_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ class RecommendOotdListWidget extends StatelessWidget {
super.key,
required this.dailyLocationWeather,
required this.feedList,
required this.isLoading,
});

final DailyLocationWeather? dailyLocationWeather;
final List<Feed> feedList;
final bool isLoading;

@override
Widget build(BuildContext context) {
Expand All @@ -32,30 +34,223 @@ class RecommendOotdListWidget extends StatelessWidget {
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
feedList.isEmpty
? const Center(
child: Text(
'아직 등록된 코디가 없어요 :( \n 가장 먼저 OOTD를 올려보세요! :D',
textAlign: TextAlign.center,
),
)
: Expanded(
isLoading
? Expanded(
child: ListView(
scrollDirection: Axis.horizontal,
children: feedList.indexed.map((e) => GestureDetector(
onTap: () => RouterStatic.pushToOotdDetail(
context,
feed: e.$2),
child: RecommendOotdWidget(
feedList: feedList,
index: e.$1,
children: List.generate(
20,
(index) => Shimmer(
linearGradient: _shimmerGradient,
child: ShimmerLoading(
isLoading: isLoading,
child: const RecommendOotdWidget(),
),
),
)).toList()
).toList(),
),
),
)
: feedList.isEmpty
? const Center(
child: Text(
'아직 등록된 코디가 없어요 :( \n 가장 먼저 OOTD를 올려보세요! :D',
textAlign: TextAlign.center,
),
)
: Expanded(
child: ListView(
scrollDirection: Axis.horizontal,
children: feedList.indexed
.map(
(e) => GestureDetector(
onTap: () => RouterStatic.pushToOotdDetail(
context,
feed: e.$2,
),
child: RecommendOotdWidget(
feedImagePath: e.$2.thumbnailImagePath,
),
),
)
.toList(),
),
),
],
),
),
);
}
}

const _shimmerGradient = LinearGradient(
colors: [
Color(0x305F5F5F),
Color(0x30FFFFFF),
Color(0x305F5F5F),
],
stops: [
0.1,
0.3,
0.4,
],
begin: Alignment(-1.0, -0.3),
end: Alignment(1.0, 0.3),
tileMode: TileMode.clamp,
);

class Shimmer extends StatefulWidget {
static ShimmerState? of(BuildContext context) {
return context.findAncestorStateOfType<ShimmerState>();
}

const Shimmer({
super.key,
required this.linearGradient,
this.child,
});

final LinearGradient linearGradient;
final Widget? child;

@override
ShimmerState createState() => ShimmerState();
}

class ShimmerState extends State<Shimmer> with SingleTickerProviderStateMixin {
late AnimationController _shimmerController;

@override
void initState() {
super.initState();

_shimmerController = AnimationController.unbounded(vsync: this)
..repeat(min: -0.5, max: 1.5, period: const Duration(milliseconds: 1000));
}

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

LinearGradient get gradient => LinearGradient(
colors: widget.linearGradient.colors,
stops: widget.linearGradient.stops,
begin: widget.linearGradient.begin,
end: widget.linearGradient.end,
transform:
_SlidingGradientTransform(slidePercent: _shimmerController.value),
);

bool get isSized =>
(context.findRenderObject() as RenderBox?)?.hasSize ?? false;

Size get size => (context.findRenderObject() as RenderBox).size;

Offset getDescendantOffset({
required RenderBox descendant,
Offset offset = Offset.zero,
}) {
final shimmerBox = context.findRenderObject() as RenderBox?;
return descendant.localToGlobal(offset, ancestor: shimmerBox);
}

Listenable get shimmerChanges => _shimmerController;

@override
Widget build(BuildContext context) {
return widget.child ?? const SizedBox();
}
}

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

final double slidePercent;

@override
Matrix4? transform(Rect bounds, {TextDirection? textDirection}) {
return Matrix4.translationValues(bounds.width * slidePercent, 0.0, 0.0);
}
}

class ShimmerLoading extends StatefulWidget {
const ShimmerLoading({
super.key,
required this.isLoading,
required this.child,
});

final bool isLoading;
final Widget child;

@override
State<ShimmerLoading> createState() => _ShimmerLoadingState();
}

class _ShimmerLoadingState extends State<ShimmerLoading> {
Listenable? _shimmerChanges;

@override
void didChangeDependencies() {
super.didChangeDependencies();
if (_shimmerChanges != null) {
_shimmerChanges!.removeListener(_onShimmerChange);
}
_shimmerChanges = Shimmer.of(context)?.shimmerChanges;
if (_shimmerChanges != null) {
_shimmerChanges!.addListener(_onShimmerChange);
}
}

@override
void dispose() {
_shimmerChanges?.removeListener(_onShimmerChange);
super.dispose();
}

void _onShimmerChange() {
if (widget.isLoading) {
setState(() {
// Update the shimmer painting.
});
}
}

@override
Widget build(BuildContext context) {
if (!widget.isLoading) {
return widget.child;
}

// Collect ancestor shimmer info.
final shimmer = Shimmer.of(context)!;
if (!shimmer.isSized) {
// The ancestor Shimmer widget has not laid
// itself out yet. Return an empty box.
return const SizedBox();
}
final shimmerSize = shimmer.size;
final gradient = shimmer.gradient;
final offsetWithinShimmer = shimmer.getDescendantOffset(
descendant: context.findRenderObject() as RenderBox,
);

return ShaderMask(
blendMode: BlendMode.srcATop,
shaderCallback: (bounds) {
return gradient.createShader(
Rect.fromLTWH(
-offsetWithinShimmer.dx,
-offsetWithinShimmer.dy,
shimmerSize.width,
shimmerSize.height,
),
);
},
child: widget.child,
);
}
}
16 changes: 7 additions & 9 deletions lib/presentation/home/component/recommend_ootd_widget.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import 'package:flutter/material.dart';
import 'package:weaco/domain/feed/model/feed.dart';
import 'package:weaco/presentation/common/component/cached_image_widget.dart';

class RecommendOotdWidget extends StatelessWidget {
const RecommendOotdWidget({
super.key,
required this.feedList,
required this.index,
this.feedImagePath,
});

final List<Feed> feedList;
final int index;
final String? feedImagePath;

@override
Widget build(BuildContext context) {
Expand All @@ -28,10 +25,11 @@ class RecommendOotdWidget extends StatelessWidget {
]),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: CachedImageWidget(
feedList[index].thumbnailImagePath,
),

child: feedImagePath != null
? CachedImageWidget(
feedImagePath!,
)
: null,
),
);
}
Expand Down
40 changes: 22 additions & 18 deletions lib/presentation/home/screen/home_screen.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:weaco/core/enum/weather_code.dart';
import 'package:weaco/core/util/reaction_util.dart';
import 'package:weaco/domain/feed/model/feed.dart';
import 'package:weaco/presentation/common/style/image_path.dart';
import 'package:weaco/core/enum/weather_code.dart';
import 'package:weaco/presentation/common/enum/exception_alert.dart';
import 'package:weaco/presentation/common/style/image_path.dart';
import 'package:weaco/presentation/common/util/alert_util.dart';
import 'package:weaco/presentation/home/component/recommend_ootd_list_widget.dart';
import 'package:weaco/presentation/home/component/weather_by_time_list_widget.dart';
Expand All @@ -27,11 +27,12 @@ class _HomeScreenState extends State<HomeScreen> {

Future.microtask(
() async {
await context.read<HomeScreenViewModel>().initHomeScreen();
if (mounted) {
final tmp = context.read<HomeScreenViewModel>().precacheList;
for (Feed e in tmp) {
await precacheImage(CachedNetworkImageProvider(e.thumbnailImagePath), context);
context.read<HomeScreenViewModel>().initHomeScreen();
final tmp = context.read<HomeScreenViewModel>().precacheList;
for (Feed e in tmp) {
if (mounted) {
await precacheImage(
CachedNetworkImageProvider(e.thumbnailImagePath), context);
}
}
},
Expand Down Expand Up @@ -67,14 +68,13 @@ class _HomeScreenState extends State<HomeScreen> {
return Scaffold(
body: switch (viewModel.status) {
HomeScreenStatus.error => const Center(child: Text('데이터를 불러올 수 없습니다.')),
HomeScreenStatus.loading =>
const Center(child: CircularProgressIndicator()),
HomeScreenStatus.idle => const SizedBox(),
HomeScreenStatus.success => Container(
HomeScreenStatus.loading || HomeScreenStatus.success => Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.fill,
image: NetworkImage(viewModel.backgroundImagePath),
image:
CachedNetworkImageProvider(viewModel.backgroundImagePath),
),
),
child: SafeArea(
Expand All @@ -96,7 +96,8 @@ class _HomeScreenState extends State<HomeScreen> {
)),
// city
Text(
viewModel.dailyLocationWeather!.location.city,
viewModel.dailyLocationWeather?.location.city ??
'-',
style: const TextStyle(
fontSize: 15,
color: Colors.white,
Expand All @@ -105,9 +106,11 @@ class _HomeScreenState extends State<HomeScreen> {
const SizedBox(height: 4),
// weather code description
Text(
WeatherCode.fromValue(
viewModel.currentWeather!.code)
.description,
viewModel.currentWeather != null
? WeatherCode.fromValue(
viewModel.currentWeather!.code)
.description
: '-',
style: const TextStyle(
fontSize: 15,
color: Colors.white,
Expand All @@ -120,7 +123,7 @@ class _HomeScreenState extends State<HomeScreen> {
)),
// current temperature
Text(
'${viewModel.currentWeather!.temperature}℃',
'${viewModel.currentWeather?.temperature ?? '-'}℃',
style: const TextStyle(
color: Colors.white,
fontSize: 60,
Expand All @@ -138,7 +141,7 @@ class _HomeScreenState extends State<HomeScreen> {
Column(
children: [
Text(
'최고 ${viewModel.dailyLocationWeather!.highTemperature}℃',
'최고 ${viewModel.dailyLocationWeather?.highTemperature ?? '-'}℃',
style: const TextStyle(
fontSize: 15,
color: Colors.white,
Expand All @@ -151,7 +154,7 @@ class _HomeScreenState extends State<HomeScreen> {
),
),
Text(
'최저 ${viewModel.dailyLocationWeather!.lowTemperature}℃',
'최저 ${viewModel.dailyLocationWeather?.lowTemperature ?? '-'}℃',
style: const TextStyle(
fontSize: 15,
color: Colors.white,
Expand Down Expand Up @@ -237,6 +240,7 @@ class _HomeScreenState extends State<HomeScreen> {

// ootd list
RecommendOotdListWidget(
isLoading: viewModel.isRecommendOotdLoading,
dailyLocationWeather: viewModel.dailyLocationWeather,
feedList: viewModel.feedList,
),
Expand Down
Loading

0 comments on commit be6644f

Please sign in to comment.