Files
2022-12-07 10:16:09 -07:00

163 lines
5.1 KiB
Dart

import 'dart:math' as math;
import 'dart:ui';
import 'package:wonders/common_libs.dart';
import 'package:wonders/logic/data/highlight_data.dart';
import 'package:wonders/ui/common/app_icons.dart';
import 'package:wonders/ui/common/controls/app_page_indicator.dart';
import 'package:wonders/ui/common/controls/simple_header.dart';
import 'package:wonders/ui/common/static_text_scale.dart';
part 'widgets/_blurred_image_bg.dart';
part 'widgets/_collapsing_carousel_item.dart';
part 'widgets/_bottom_text_content.dart';
class ArtifactCarouselScreen extends StatefulWidget {
final WonderType type;
const ArtifactCarouselScreen({Key? key, required this.type}) : super(key: key);
@override
State<ArtifactCarouselScreen> createState() => _ArtifactScreenState();
}
class _ArtifactScreenState extends State<ArtifactCarouselScreen> {
PageController? _pageController;
final _currentPage = ValueNotifier<double>(9999);
late final List<HighlightData> _artifacts = HighlightData.forWonder(widget.type);
late final _currentArtifactIndex = ValueNotifier<int>(_wrappedPageIndex);
int get _wrappedPageIndex => _currentPage.value.round() % _artifacts.length;
void _handlePageChanged() {
_currentPage.value = _pageController?.page ?? 0;
_currentArtifactIndex.value = _wrappedPageIndex;
}
void _handleSearchTap() => context.push(ScreenPaths.search(widget.type));
void _handleArtifactTap(int index) {
int delta = index - _currentPage.value.round();
if (delta == 0) {
HighlightData data = _artifacts[index % _artifacts.length];
context.push(ScreenPaths.artifact(data.artifactId));
} else {
_pageController?.animateToPage(
_currentPage.value.round() + delta,
duration: $styles.times.fast,
curve: Curves.easeOut,
);
}
}
@override
Widget build(BuildContext context) {
bool shortMode = context.heightPx < 800;
final double bottomHeight = shortMode ? 240 : 340;
// Allow objects to become wider as the screen becomes tall, this allows
// them to grow taller as well, filling the available space better.
double itemHeight = (context.heightPx - 200 - bottomHeight).clamp(250, 400);
double itemWidth = itemHeight * .666;
// TODO: This could be optimized to only run if the size has changed...is it worth it?
_pageController?.dispose();
_pageController = PageController(
viewportFraction: itemWidth / context.widthPx,
initialPage: _currentPage.value.round(),
);
_pageController?.addListener(_handlePageChanged);
final pages = _artifacts.map((e) {
return Padding(
padding: EdgeInsets.all(10),
child: _DoubleBorderImage(e),
);
}).toList();
return Stack(
children: [
/// Blurred Bg
Positioned.fill(
child: ValueListenableBuilder<int>(
valueListenable: _currentArtifactIndex,
builder: (_, value, __) {
return _BlurredImageBg(url: _artifacts[value].imageUrl);
}),
),
/// BgCircle
_buildBgCircle(bottomHeight),
/// Carousel Items
PageView.builder(
controller: _pageController,
itemBuilder: (_, index) {
final wrappedIndex = index % pages.length;
final child = pages[wrappedIndex];
return ValueListenableBuilder<double>(
valueListenable: _currentPage,
builder: (_, value, __) {
final int offset = (value.round() - index).abs();
return _CollapsingCarouselItem(
width: itemWidth,
bottom: bottomHeight - 20,
indexOffset: min(3, offset),
onPressed: () => _handleArtifactTap(index),
title: _artifacts[wrappedIndex].title,
child: child,
);
},
);
},
),
/// Bottom Text
BottomCenter(
child: ValueListenableBuilder<int>(
valueListenable: _currentArtifactIndex,
builder: (_, value, __) => _BottomTextContent(
artifact: _artifacts[value],
height: bottomHeight,
shortMode: shortMode,
state: this,
),
),
),
/// Header
SimpleHeader(
$strings.artifactsTitleArtifacts,
showBackBtn: false,
isTransparent: true,
trailing: (context) => CircleBtn(
semanticLabel: $strings.artifactsButtonBrowse,
onPressed: _handleSearchTap,
child: AppIcon(AppIcons.search),
),
),
],
);
}
OverflowBox _buildBgCircle(double height) {
const double size = 1500;
return OverflowBox(
maxWidth: size,
maxHeight: size,
child: Transform.translate(
offset: Offset(0, size / 2),
child: Container(
decoration: BoxDecoration(
color: $styles.colors.offWhite.withOpacity(0.8),
borderRadius: BorderRadius.vertical(top: Radius.circular(size * .45)),
),
),
),
);
}
@override
void dispose() {
_pageController?.dispose();
super.dispose();
}
}