전역 구조 리팩터링 및 테스트 확장

This commit is contained in:
JiWoong Sul
2025-09-29 01:51:47 +09:00
parent c00c0c9ab2
commit fef7108479
70 changed files with 7709 additions and 3185 deletions

View File

@@ -1,6 +1,98 @@
import 'package:flutter/widgets.dart';
const double desktopBreakpoint = 1200;
const double tabletBreakpoint = 960;
enum DeviceBreakpoint { mobile, tablet, desktop }
DeviceBreakpoint breakpointForWidth(double width) {
if (width >= desktopBreakpoint) {
return DeviceBreakpoint.desktop;
}
if (width >= tabletBreakpoint) {
return DeviceBreakpoint.tablet;
}
return DeviceBreakpoint.mobile;
}
bool isDesktop(double width) => width >= desktopBreakpoint;
bool isTablet(double width) => width >= tabletBreakpoint && width < desktopBreakpoint;
bool isTablet(double width) =>
width >= tabletBreakpoint && width < desktopBreakpoint;
bool isMobile(double width) => width < tabletBreakpoint;
bool isDesktopContext(BuildContext context) =>
isDesktop(MediaQuery.of(context).size.width);
bool isTabletContext(BuildContext context) =>
isTablet(MediaQuery.of(context).size.width);
bool isMobileContext(BuildContext context) =>
isMobile(MediaQuery.of(context).size.width);
class ResponsiveBreakpoints {
ResponsiveBreakpoints._(this.width) : breakpoint = breakpointForWidth(width);
final double width;
final DeviceBreakpoint breakpoint;
bool get isMobile => breakpoint == DeviceBreakpoint.mobile;
bool get isTablet => breakpoint == DeviceBreakpoint.tablet;
bool get isDesktop => breakpoint == DeviceBreakpoint.desktop;
static ResponsiveBreakpoints of(BuildContext context) {
final size = MediaQuery.of(context).size;
return ResponsiveBreakpoints._(size.width);
}
}
class ResponsiveLayoutBuilder extends StatelessWidget {
const ResponsiveLayoutBuilder({
super.key,
required this.mobile,
this.tablet,
required this.desktop,
});
final WidgetBuilder mobile;
final WidgetBuilder? tablet;
final WidgetBuilder desktop;
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final breakpoint = breakpointForWidth(constraints.maxWidth);
switch (breakpoint) {
case DeviceBreakpoint.mobile:
return mobile(context);
case DeviceBreakpoint.tablet:
final tabletBuilder = tablet ?? desktop;
return tabletBuilder(context);
case DeviceBreakpoint.desktop:
return desktop(context);
}
},
);
}
}
class ResponsiveVisibility extends StatelessWidget {
const ResponsiveVisibility({
super.key,
required this.child,
this.replacement = const SizedBox.shrink(),
this.visibleOn = const {
DeviceBreakpoint.mobile,
DeviceBreakpoint.tablet,
DeviceBreakpoint.desktop,
},
});
final Widget child;
final Widget replacement;
final Set<DeviceBreakpoint> visibleOn;
@override
Widget build(BuildContext context) {
final breakpoint = ResponsiveBreakpoints.of(context).breakpoint;
return visibleOn.contains(breakpoint) ? child : replacement;
}
}