first commit
This commit is contained in:
21
lib/common/widgets/bottom_safe_space.dart
Normal file
21
lib/common/widgets/bottom_safe_space.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
// Flutter imports:
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class BottomSafeSpace extends StatelessWidget {
|
||||
///
|
||||
/// Отступ от нижней границы экрана
|
||||
///
|
||||
/// Для iOS значение будет не нулевое если есть "полоска"
|
||||
/// Для Android в основном будет 0
|
||||
///
|
||||
const BottomSafeSpace({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: MediaQuery.of(context).padding.bottom,
|
||||
);
|
||||
}
|
||||
}
|
||||
24
lib/common/widgets/loose_focus.dart
Normal file
24
lib/common/widgets/loose_focus.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
// Flutter imports:
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LooseFocus extends StatelessWidget {
|
||||
///
|
||||
/// Теряет фокус если тапнули по пустой области
|
||||
///
|
||||
const LooseFocus({
|
||||
required this.child,
|
||||
super.key,
|
||||
});
|
||||
|
||||
/// Потомок
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => FocusScope.of(context).requestFocus(FocusNode()),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
68
lib/common/widgets/swipe_gesture.dart
Normal file
68
lib/common/widgets/swipe_gesture.dart
Normal file
@@ -0,0 +1,68 @@
|
||||
// Flutter imports:
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
///
|
||||
/// Направления свайпов
|
||||
///
|
||||
enum SwipeDirection {
|
||||
///
|
||||
/// Свайп вниз
|
||||
///
|
||||
DOWN,
|
||||
|
||||
///
|
||||
/// Свайп вверх
|
||||
///
|
||||
UP,
|
||||
}
|
||||
|
||||
class SwipeGesture extends StatelessWidget {
|
||||
///
|
||||
/// Отслеживание свайпа по виджету
|
||||
///
|
||||
const SwipeGesture({
|
||||
required this.swipe,
|
||||
required this.onSwipe,
|
||||
required this.child,
|
||||
super.key,
|
||||
});
|
||||
|
||||
/// Направление движения
|
||||
final SwipeDirection swipe;
|
||||
|
||||
/// Обработка свайпа
|
||||
final VoidCallback onSwipe;
|
||||
|
||||
/// Потомок
|
||||
final Widget child;
|
||||
|
||||
void _onVerticalDragUpdate(DragUpdateDetails e) {
|
||||
const int sensitivity = 3;
|
||||
|
||||
switch (swipe) {
|
||||
case SwipeDirection.DOWN:
|
||||
if (e.delta.dy > sensitivity) {
|
||||
onSwipe();
|
||||
}
|
||||
|
||||
break;
|
||||
case SwipeDirection.UP:
|
||||
if (e.delta.dy < sensitivity) {
|
||||
onSwipe();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
dragStartBehavior: DragStartBehavior.start,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onVerticalDragUpdate: _onVerticalDragUpdate,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
256
lib/common/widgets/typography.dart
Normal file
256
lib/common/widgets/typography.dart
Normal file
@@ -0,0 +1,256 @@
|
||||
// Flutter imports:
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
|
||||
// Package imports:
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
export '../../common/typography.dart';
|
||||
|
||||
abstract class TypographyType {
|
||||
///
|
||||
/// Размер шрифта
|
||||
///
|
||||
double get size;
|
||||
|
||||
///
|
||||
/// Получение [TextStyle] для типа шрифта
|
||||
///
|
||||
TextStyle get style;
|
||||
}
|
||||
|
||||
abstract class TypographyTypeRegular extends TypographyType {
|
||||
///
|
||||
/// Высота линии
|
||||
///
|
||||
double get height => 1.21;
|
||||
|
||||
@override
|
||||
TextStyle get style {
|
||||
return GoogleFonts.inter(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: size.sp,
|
||||
height: height,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TypographyTypeMedium extends TypographyType {
|
||||
///
|
||||
/// Высота линии
|
||||
///
|
||||
double get height => 1.15;
|
||||
|
||||
@override
|
||||
TextStyle get style {
|
||||
return GoogleFonts.inter(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: size.sp,
|
||||
height: height,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TypographyTypeSemiBold extends TypographyType {
|
||||
///
|
||||
/// Высота линии
|
||||
///
|
||||
double get height => 1.21;
|
||||
|
||||
@override
|
||||
TextStyle get style {
|
||||
return GoogleFonts.inter(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: size.sp,
|
||||
height: height,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TypographyTypeBold extends TypographyType {
|
||||
///
|
||||
/// Высота линии
|
||||
///
|
||||
double get height => 1.21;
|
||||
|
||||
@override
|
||||
TextStyle get style {
|
||||
return GoogleFonts.inter(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: size.sp,
|
||||
height: height,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TypographyTypeHeadBold extends TypographyType {
|
||||
///
|
||||
/// Высота линии
|
||||
///
|
||||
double get height => 1.36;
|
||||
|
||||
@override
|
||||
TextStyle get style {
|
||||
return GoogleFonts.inter(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: size.sp,
|
||||
height: height,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Дополнительные возможности [AppTypography]
|
||||
///
|
||||
enum TypographyFeature {
|
||||
///
|
||||
/// Использование [Text]
|
||||
///
|
||||
DEFAULT,
|
||||
|
||||
///
|
||||
/// Использование [RichText]
|
||||
///
|
||||
RICH,
|
||||
|
||||
///
|
||||
/// Использование [SelectableText]
|
||||
///
|
||||
SELECTABLE,
|
||||
}
|
||||
|
||||
///
|
||||
/// Виджет для отображения текста в приложении
|
||||
///
|
||||
/// Тесно использует [TypographyType]
|
||||
/// На его основе выбирается стиль текста
|
||||
///
|
||||
/// Важно: планшетная верстка не учитывается в стилях - меняется сам стиль,
|
||||
/// поэтому контролировать нужно извне
|
||||
///
|
||||
class AppTypography extends StatelessWidget {
|
||||
const AppTypography(
|
||||
this.text, {
|
||||
this.type,
|
||||
this.color,
|
||||
this.maxLines = 1,
|
||||
this.textAlign = TextAlign.left,
|
||||
this.fontWeight,
|
||||
this.overflow = TextOverflow.ellipsis,
|
||||
this.height,
|
||||
this.softWrap,
|
||||
this.textDecoration = TextDecoration.none,
|
||||
super.key,
|
||||
}) : typographyFeature = TypographyFeature.DEFAULT,
|
||||
children = null;
|
||||
|
||||
const AppTypography.rich(
|
||||
this.text, {
|
||||
this.type,
|
||||
this.color,
|
||||
this.maxLines,
|
||||
this.textAlign = TextAlign.left,
|
||||
this.fontWeight,
|
||||
this.overflow,
|
||||
this.height,
|
||||
this.softWrap,
|
||||
this.textDecoration = TextDecoration.none,
|
||||
this.children,
|
||||
super.key,
|
||||
}) : typographyFeature = TypographyFeature.RICH;
|
||||
|
||||
const AppTypography.selectable(
|
||||
this.text, {
|
||||
this.type,
|
||||
this.color,
|
||||
this.maxLines,
|
||||
this.textAlign = TextAlign.left,
|
||||
this.fontWeight,
|
||||
this.overflow,
|
||||
this.height,
|
||||
this.softWrap,
|
||||
this.textDecoration = TextDecoration.none,
|
||||
super.key,
|
||||
}) : typographyFeature = TypographyFeature.SELECTABLE,
|
||||
children = null;
|
||||
|
||||
/// Текст
|
||||
final String text;
|
||||
|
||||
/// Стиль текста
|
||||
final TypographyType? type;
|
||||
|
||||
/// Цвет текста
|
||||
final Color? color;
|
||||
|
||||
/// Кол-во линий
|
||||
final int? maxLines;
|
||||
|
||||
/// Направление текста
|
||||
final TextAlign textAlign;
|
||||
|
||||
/// Жирность шрифта
|
||||
final FontWeight? fontWeight;
|
||||
|
||||
/// Overflow
|
||||
final TextOverflow? overflow;
|
||||
|
||||
/// Межстрочный интервал
|
||||
final double? height;
|
||||
|
||||
/// Мягкий перенос
|
||||
final bool? softWrap;
|
||||
|
||||
/// Декорация текста
|
||||
final TextDecoration textDecoration;
|
||||
|
||||
/// Использование фич типографии
|
||||
final TypographyFeature typographyFeature;
|
||||
|
||||
/// Список [TextSpan]
|
||||
final List<InlineSpan>? children;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final DefaultTextStyle textStyle = DefaultTextStyle.of(context);
|
||||
|
||||
final TextStyle computeTextStyle =
|
||||
type == null
|
||||
? textStyle.style
|
||||
: type!.style.copyWith(
|
||||
color: color ?? textStyle.style.color,
|
||||
fontWeight: fontWeight,
|
||||
height: height,
|
||||
decoration: textDecoration,
|
||||
);
|
||||
|
||||
switch (typographyFeature) {
|
||||
case TypographyFeature.DEFAULT:
|
||||
return Text(
|
||||
text,
|
||||
maxLines: maxLines,
|
||||
textAlign: textAlign,
|
||||
overflow: overflow,
|
||||
softWrap: softWrap,
|
||||
style: computeTextStyle,
|
||||
);
|
||||
|
||||
case TypographyFeature.RICH:
|
||||
return RichText(
|
||||
text: TextSpan(
|
||||
text: text,
|
||||
children: children,
|
||||
style: computeTextStyle,
|
||||
),
|
||||
);
|
||||
|
||||
case TypographyFeature.SELECTABLE:
|
||||
return SelectableText(
|
||||
text,
|
||||
maxLines: maxLines,
|
||||
textAlign: textAlign,
|
||||
style: computeTextStyle,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
lib/common/widgets/typography_span.dart
Normal file
15
lib/common/widgets/typography_span.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
// Flutter imports:
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TypographySpan extends TextSpan {
|
||||
///
|
||||
/// Упрощенный вариант [TextSpan]
|
||||
///
|
||||
const TypographySpan(
|
||||
this.text, {
|
||||
super.style,
|
||||
});
|
||||
|
||||
// ignore: annotate_overrides, overridden_fields
|
||||
final String text;
|
||||
}
|
||||
35
lib/common/widgets/w_if.dart
Normal file
35
lib/common/widgets/w_if.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
// Flutter imports:
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
///
|
||||
/// Виджет условной отрисовки
|
||||
///
|
||||
class Wif extends StatelessWidget {
|
||||
/// Условие по которому будет происходить отрисовка
|
||||
final bool condition;
|
||||
|
||||
/// Построение содержимого
|
||||
final WidgetBuilder builder;
|
||||
|
||||
/// Виджет если условие не удовлетворительно
|
||||
final WidgetBuilder? fallback;
|
||||
|
||||
///
|
||||
/// Виджет условной отрисовки
|
||||
///
|
||||
const Wif({
|
||||
required this.condition,
|
||||
required this.builder,
|
||||
this.fallback,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return condition
|
||||
? builder(context)
|
||||
: fallback != null
|
||||
? fallback!(context)
|
||||
: const Offstage();
|
||||
}
|
||||
}
|
||||
61
lib/common/widgets/wspace.dart
Normal file
61
lib/common/widgets/wspace.dart
Normal file
@@ -0,0 +1,61 @@
|
||||
// Flutter imports:
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:remever/components/extensions/context.dart';
|
||||
|
||||
///
|
||||
/// Разделитель между виджетами
|
||||
///
|
||||
class HSpace extends StatelessWidget {
|
||||
/// Высота разделителя
|
||||
final double height;
|
||||
|
||||
/// Флаг что необходимо использовать [SliverToBoxAdapter]
|
||||
final bool useSliver;
|
||||
|
||||
///
|
||||
/// Разделитель между виджетами
|
||||
///
|
||||
const HSpace(this.height, {super.key}) : useSliver = false;
|
||||
|
||||
const HSpace.sliver(this.height, {super.key}) : useSliver = true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget child = SizedBox(height: height.h);
|
||||
|
||||
if (useSliver) {
|
||||
child = SliverToBoxAdapter(child: child);
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Разделитель между виджетами
|
||||
///
|
||||
class WSpace extends StatelessWidget {
|
||||
/// Ширина разделителя
|
||||
final double width;
|
||||
|
||||
/// Флаг что необходимо использовать [SliverToBoxAdapter]
|
||||
final bool useSliver;
|
||||
|
||||
///
|
||||
/// Разделитель между виджетами
|
||||
///
|
||||
const WSpace(this.width, {super.key}) : useSliver = false;
|
||||
|
||||
const WSpace.sliver(this.width, {super.key}) : useSliver = true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget child = SizedBox(width: width.w);
|
||||
|
||||
if (useSliver) {
|
||||
child = SliverToBoxAdapter(child: child);
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user