feat: 首页,收藏页的UI,字体 组件封装

This commit is contained in:
phoenix--zhang 2026-07-03 15:38:01 +08:00
parent f233f1c642
commit 7f329aef44
32 changed files with 1110 additions and 41 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/imgs/btn_bg_rad1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

BIN
assets/imgs/btn_bg_rad2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 885 B

BIN
assets/imgs/btn_two_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
assets/imgs/cate_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
assets/imgs/home_first.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

BIN
assets/imgs/home_search.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
assets/imgs/hot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

BIN
assets/imgs/search_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -9,6 +9,8 @@ class ConstColor {
static Color btWhite = Color(0xffEEEEEE);
// #89F011
static Color btGreen = Color(0xff89F011);
// black
static Color black = Color(0xff222222);
static Color withValue(String value) {
String colorValue = value.replaceAll('#', '');

View File

@ -0,0 +1,11 @@
class ConstString {
}
class ConstFont {
static const String montBlack = 'Montserrat-Black';
static const String montBold = 'Montserrat-Bold';
static const String montMedium = 'Montserrat-Medium';
static const String montRegular = 'Montserrat-Regular';
}

View File

@ -16,6 +16,25 @@ class ConstImage {
static const String btMineSel = "assets/main/bt_mine_seled.png";
static const String spaceImg = "assets/main/space_img.png";
/// imgs
static const String searchBg = "assets/imgs/search_bg.png";
// home_search top_logo
static const String homeSearch = "assets/imgs/home_search.png";
static const String homeTopLogo = "assets/imgs/home_top_logo.png";
static const String homeFirst = "assets/imgs/home_first.png";
static const String homeCurrentTag = "assets/imgs/home_current_tag.png";
static const String homeRank = "assets/imgs/home_rank_bg.png";
// btn_two_bg
static const String btnTwoBg = "assets/imgs/btn_two_bg.png";
// cate_bg
static const String cateBg = "assets/imgs/cate_bg.png";
// hot
static const String hot = "assets/imgs/hot.png";
// btn_bg_rad1
static const String btnBgRad1 = "assets/imgs/btn_bg_rad1.png";
static const String btnBgRad2 = "assets/imgs/btn_bg_rad2.png";
}

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
@ -9,6 +10,7 @@ void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const RootApp());
}
@ -38,38 +40,45 @@ class _RootAppState extends State<RootApp> with WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: Size(375, 812),
child: GetMaterialApp(
debugShowCheckedModeBanner: false,
// translations: AppLanguage(),
locale: widget.locale ?? Get.deviceLocale,
fallbackLocale: const Locale('en'),
initialBinding: AppBindings(),
// initialRoute: SkyPages.initial,
// getPages: SkyPages.routes,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: ConstColor.themeColor,
),
appBarTheme: AppBarTheme(elevation: 0, centerTitle: true),
splashColor: Colors.transparent,
useMaterial3: true,
scaffoldBackgroundColor: Colors.white,
),
builder: FlutterSmartDialog.init(
builder: (context, child) {
return MediaQuery(
data: MediaQuery.of(
context,
).copyWith(textScaler: TextScaler.linear(1.0)),
child: child!,
);
},
),
home: widget.home ?? RootPage(),
onInit: () {},
return AnnotatedRegion<SystemUiOverlayStyle>(
value: const SystemUiOverlayStyle(
statusBarColor: Colors.white,
statusBarBrightness: Brightness.dark, // 🔥 iOS
statusBarIconBrightness: Brightness.dark, // Android
),
child: ScreenUtilInit(
designSize: Size(375, 812),
child: GetMaterialApp(
debugShowCheckedModeBanner: false,
// translations: AppLanguage(),
locale: widget.locale ?? Get.deviceLocale,
fallbackLocale: const Locale('en'),
initialBinding: AppBindings(),
// initialRoute: SkyPages.initial,
// getPages: SkyPages.routes,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: ConstColor.themeColor,
),
appBarTheme: AppBarTheme(elevation: 0, centerTitle: true),
splashColor: Colors.transparent,
useMaterial3: true,
scaffoldBackgroundColor: Colors.white,
),
builder: FlutterSmartDialog.init(
builder: (context, child) {
return MediaQuery(
data: MediaQuery.of(
context,
).copyWith(textScaler: TextScaler.linear(1.0)),
child: child!,
);
},
),
home: widget.home ?? RootPage(),
onInit: () {},
),
)
);
}

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import '../../../tools/widgets/app_bg_page.dart';
class CollectionPage extends StatefulWidget {
const CollectionPage({super.key});
@ -10,9 +12,407 @@ class CollectionPage extends StatefulWidget {
}
class _RootPageState extends State<CollectionPage> {
static const List<_DramaItem> _favoriteItems = [
_DramaItem(
title: 'An Uninvited Guest',
imageUrl:
'https://static.goodshort.com/upload/serial/cover/2024/09/26/66f4d0ce67383.jpg',
currentEpisode: 1,
totalEpisode: 51,
),
_DramaItem(
title: 'Vanished Without a Word',
imageUrl:
'https://static.goodshort.com/upload/serial/cover/2024/06/11/6667f17c9df69.jpg',
currentEpisode: 1,
totalEpisode: 51,
),
_DramaItem(
title: 'Dawn of Stardom',
imageUrl:
'https://static.goodshort.com/upload/serial/cover/2024/07/19/669a15b8a2e28.jpg',
currentEpisode: 1,
totalEpisode: 51,
),
_DramaItem(
title: 'Marry Me Twice',
imageUrl:
'https://static.goodshort.com/upload/serial/cover/2024/04/30/6630c89b746a8.jpg',
currentEpisode: 1,
totalEpisode: 51,
),
_DramaItem(
title: 'Not Your Bride',
imageUrl:
'https://static.goodshort.com/upload/serial/cover/2024/05/22/664daf9d042d4.jpg',
currentEpisode: 1,
totalEpisode: 51,
),
_DramaItem(
title: "Husband's Hidden Crown",
imageUrl:
'https://static.goodshort.com/upload/serial/cover/2024/08/12/66b9dfd8182cc.jpg',
currentEpisode: 1,
totalEpisode: 51,
),
_DramaItem(
title: 'In the City of Love',
imageUrl:
'https://static.goodshort.com/upload/serial/cover/2024/07/24/66a0b88fda163.jpg',
currentEpisode: 1,
totalEpisode: 51,
),
_DramaItem(
title: "Mommy's Genius Squad",
imageUrl:
'https://static.goodshort.com/upload/serial/cover/2024/05/17/664716f187b2b.jpg',
currentEpisode: 1,
totalEpisode: 51,
),
_DramaItem(
title: 'Burned by Love',
imageUrl:
'https://static.goodshort.com/upload/serial/cover/2024/07/05/66876b720daf4.jpg',
currentEpisode: 1,
totalEpisode: 51,
),
];
static const List<_DramaItem> _historyItems = [
_DramaItem(
title: 'Shadows of Vengeance',
imageUrl:
'https://static.goodshort.com/upload/serial/cover/2024/09/10/66e01c7ab24ab.jpg',
currentEpisode: 1,
totalEpisode: 51,
),
_DramaItem(
title: "CEO's Naughty Wife Is Hidden Rich",
imageUrl:
'https://static.goodshort.com/upload/serial/cover/2024/08/02/66accc37da3a5.jpg',
currentEpisode: 1,
totalEpisode: 51,
),
_DramaItem(
title: 'Her Veiled Protector',
imageUrl:
'https://static.goodshort.com/upload/serial/cover/2024/06/28/667e7312bc731.jpg',
currentEpisode: 1,
totalEpisode: 51,
),
_DramaItem(
title: 'Love Deep As The Ocean',
imageUrl:
'https://static.goodshort.com/upload/serial/cover/2024/06/14/666c06fb54983.jpg',
currentEpisode: 1,
totalEpisode: 51,
),
_DramaItem(
title: 'Not Your Bride',
imageUrl:
'https://static.goodshort.com/upload/serial/cover/2024/05/22/664daf9d042d4.jpg',
currentEpisode: 1,
totalEpisode: 51,
),
_DramaItem(
title: 'Shadows of Vengeance',
imageUrl:
'https://static.goodshort.com/upload/serial/cover/2024/09/10/66e01c7ab24ab.jpg',
currentEpisode: 1,
totalEpisode: 51,
),
_DramaItem(
title: 'Shadows of Vengeance',
imageUrl:
'https://static.goodshort.com/upload/serial/cover/2024/09/10/66e01c7ab24ab.jpg',
currentEpisode: 1,
totalEpisode: 51,
),
];
bool _showHistory = false;
@override
Widget build(BuildContext context) {
return Scaffold();
return AppBgPage(
child: SafeArea(
bottom: false,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildHeader(),
Expanded(
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 180),
child: _showHistory
? _buildHistoryList()
: _buildFavoritesGrid(),
),
),
],
),
),
);
}
}
Widget _buildHeader() {
return Padding(
padding: const EdgeInsets.fromLTRB(30, 18, 26, 12),
child: Row(
children: [
_buildTab(
'Favorites',
active: !_showHistory,
onTap: () {
setState(() {
_showHistory = false;
});
},
),
const SizedBox(width: 16),
_buildTab(
'History',
active: _showHistory,
onTap: () {
setState(() {
_showHistory = true;
});
},
),
const Spacer(),
const Icon(Icons.edit_outlined, color: Colors.white, size: 22),
],
),
);
}
Widget _buildTab(
String title, {
required bool active,
required VoidCallback onTap,
}) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
color: active
? Colors.white
: Colors.white.withValues(alpha: 0.42),
fontSize: 16,
fontFamily: 'Montserrat-Bold',
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 5),
AnimatedContainer(
duration: const Duration(milliseconds: 180),
width: active ? 54 : 0,
height: 2,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(1),
),
),
],
),
);
}
Widget _buildFavoritesGrid() {
return GridView.builder(
key: const ValueKey('favorites'),
padding: const EdgeInsets.fromLTRB(30, 12, 14, 96),
itemCount: _favoriteItems.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 8,
mainAxisSpacing: 14,
childAspectRatio: 0.55,
),
itemBuilder: (context, index) {
return _FavoriteCard(item: _favoriteItems[index]);
},
);
}
Widget _buildHistoryList() {
return ListView.separated(
key: const ValueKey('history'),
padding: const EdgeInsets.fromLTRB(30, 8, 20, 96),
itemCount: _historyItems.length,
separatorBuilder: (context, index) => const SizedBox(height: 13),
itemBuilder: (context, index) {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 30,
child: Icon(
Icons.bookmark_border,
color: Colors.white.withValues(alpha: 0.28),
size: 32,
),
),
const SizedBox(width: 18),
Expanded(child: _HistoryItem(item: _historyItems[index])),
],
);
},
);
}
}
class _FavoriteCard extends StatelessWidget {
const _FavoriteCard({required this.item});
final _DramaItem item;
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(2),
child: Image.network(
item.imageUrl,
width: constraints.maxWidth,
height: constraints.maxWidth * 1.33,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(
width: constraints.maxWidth,
height: constraints.maxWidth * 1.33,
color: Colors.white.withValues(alpha: 0.12),
);
},
),
),
const SizedBox(height: 8),
Text(
item.title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.white,
fontSize: 13,
height: 1.18,
fontFamily: 'Montserrat-Bold',
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 8),
Text(
item.episodeText,
style: TextStyle(
color: Colors.white.withValues(alpha: 0.48),
fontSize: 11,
fontFamily: 'Montserrat-Medium',
fontWeight: FontWeight.w500,
),
),
],
);
},
);
}
}
class _HistoryItem extends StatelessWidget {
const _HistoryItem({required this.item});
final _DramaItem item;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 68,
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(2),
child: Image.network(
item.imageUrl,
width: 51,
height: 68,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(
width: 51,
height: 68,
color: Colors.white.withValues(alpha: 0.12),
);
},
),
),
const SizedBox(width: 15),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: Colors.white,
fontSize: 14,
height: 1.22,
fontFamily: 'Montserrat-Bold',
fontWeight: FontWeight.w700,
),
),
const SizedBox(height: 8),
Text.rich(
TextSpan(
children: [
TextSpan(
text: 'EP.${item.currentEpisode}',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
),
),
TextSpan(text: ' /EP.${item.totalEpisode}'),
],
),
style: TextStyle(
color: Colors.white.withValues(alpha: 0.45),
fontSize: 12,
fontFamily: 'Montserrat-Medium',
fontWeight: FontWeight.w500,
),
),
],
),
),
],
),
);
}
}
class _DramaItem {
const _DramaItem({
required this.title,
required this.imageUrl,
required this.currentEpisode,
required this.totalEpisode,
});
final String title;
final String imageUrl;
final int currentEpisode;
final int totalEpisode;
String get episodeText => 'EP.$currentEpisode/EP.$totalEpisode';
}

View File

@ -1,4 +1,9 @@
import 'package:flutter/material.dart';
import 'package:novyronst/common/help/help_image.dart';
import 'package:novyronst/pages/first/widgets/home_first_view.dart';
import 'package:novyronst/pages/first/widgets/search_view.dart';
import '../../../tools/widgets/app_bg_page.dart';
class FirstPage extends StatefulWidget {
const FirstPage({super.key});
@ -13,6 +18,27 @@ class _RootPageState extends State<FirstPage> {
@override
Widget build(BuildContext context) {
return Scaffold();
return AppBgPage(
child: Column(
children: [
HomeSearchView(),
Expanded(
child: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: HomeFirstView(),
),
SliverPadding(
padding: const EdgeInsets.only(left: 16, right: 10, top: 16),
sliver: SliverToBoxAdapter(
child: HelpImage.asset(ConstImage.homeRank),
)
)
],
),
)
],
),
);
}
}

View File

@ -0,0 +1,149 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:novyronst/pages/widgets/cut_corner_clipper.dart';
import 'package:novyronst/pages/widgets/ny_progress_widget.dart';
import 'package:novyronst/pages/widgets/ny_tag_widget.dart';
import 'package:novyronst/root/lib_export.dart';
import 'package:novyronst/tools/widgets/novy_button_bg.dart';
import 'package:novyronst/tools/widgets/novy_com_widget.dart';
import 'package:novyronst/tools/widgets/novy_img_bg.dart';
class HomeFirstView extends StatefulWidget {
const HomeFirstView({super.key});
@override
State<StatefulWidget> createState() {
return _HomeFirstViewState();
}
}
class _HomeFirstViewState extends State<HomeFirstView> {
bool _hasHistory = true;
@override
Widget build(BuildContext context) {
const double _space = 25;
return Stack(
children: [
// 151
SizedBox(
height: 151 - _space + 200 + 52 + 20,
),
HelpImage.asset(ConstImage.homeFirst, width: AppScreen.screenWidth, fit: BoxFit.fill),
Positioned(
top: 151-_space, left: 16, right: 16,
child: Column(
children: [
CutCornerClipper(
width: AppScreen.screenWidth - 32,
height: 204,
cutSize: 80,
child: _buildVideoItem(),
),
const SizedBox(height: 16),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(3, (index) {
return Container(
margin: const EdgeInsets.only(right: 8),
child: NovyImgBg(imgBg: ConstImage.cateBg,
height: 52,
child: Stack(
children:[
NovyText('ClassPp', color: ConstColor.btGreen,
fontSize: 21,
textAlign: TextAlign.center,
fontFamily: ConstFont.montBlack,
shadow: Shadow(
offset: const Offset(1, 1), // X: 1, Y: 1
blurRadius: 0, // Blur: 0
color: Colors.black, // #000000
),
),
Positioned(
bottom: 0, left: 0, right: 0,
child: Container(
height: 2,
color: Colors.black,
),
)
]
),
),
);
}),
),
),
],
)
)
],
);
}
_buildVideoItem() {
return Padding(
padding: const EdgeInsets.all(8),
child: Column(
children: [
Row(
children: [
NovyText('Current Viewing Progress',
color: Colors.black,
fontSize: 16,
fontFamily: ConstFont.montBlack,
),
const SizedBox(width: 10),
HelpImage.asset(ConstImage.homeCurrentTag, height: 10)
],
),
const SizedBox(height: 10),
Row(
children: [
Container(
color: Colors.grey.withAlpha(200),
width: 60, height: 90,
),
const SizedBox(width: 12),
SizedBox(
height: 90,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
NovyText('Jdksyjs dsaoy',
color: Colors.black,
fontSize: 14,
fontFamily: ConstFont.montMedium,
),
const SizedBox(height: 4),
NovyText('Jdksyjs dsaoy dasku dsaldsa dljas',
color: Colors.grey,
fontSize: 10,
fontFamily: ConstFont.montRegular,
maxLines: 2,
),
Spacer(),
Row(
children: [
NyTagWidget(text: 'Safiray'),
const SizedBox(width: 8),
NyHotWidget(text: '12.4k'),
],
)
],
),
)
],
),
const SizedBox(height: 10),
NyProgressWidget(),
const SizedBox(height: 6),
NovyButtonBg(text: 'Keep Watching', type: 2, width: 327.w,)
],
),
);
}
}

View File

@ -0,0 +1,78 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:novyronst/root/lib_export.dart';
class HomeSearchView extends StatefulWidget {
const HomeSearchView({super.key});
@override
State<StatefulWidget> createState() {
return _SearchViewState();
}
}
class _SearchViewState extends State<HomeSearchView> {
final _searchController = TextEditingController();
@override
Widget build(BuildContext context) {
return Container(
height: AppScreen.navBarHeight,
padding: EdgeInsets.only(top: AppScreen.statusBarHeight,
left: 16, right: 16,
bottom: 5
),
child: SizedBox(
height: 36,
child: Row(
children: [
HelpImage.asset(ConstImage.homeTopLogo, width: 36),
const SizedBox(width: 16),
Expanded(
child: Stack(
children: [
HelpImage.asset(ConstImage.searchBg, height: 36, fit: BoxFit.fill),
Padding(
padding: EdgeInsets.only(left: 11),
child: Row(
children: [
Icon(Icons.search, color: Colors.white, size: 24),
Expanded(child: CupertinoTextField(
controller: _searchController,
// focusNode: widget.focusNode,
decoration: BoxDecoration(),
placeholder: 'What are...... ',
placeholderStyle: TextStyle(
fontSize: 13,
color: Colors.white.withAlpha(127),
),
style: TextStyle(
fontSize: 14,
color: Colors.white,
),
textInputAction: TextInputAction.search,
onEditingComplete: () {
},
),),
HelpImage.asset(ConstImage.homeSearch, width: 78, height: 36, fit: BoxFit.fill)
],
),
)
],
),
)
],
),
),
);
}
}

View File

@ -0,0 +1,101 @@
import 'package:flutter/material.dart';
class CutCornerClipper extends StatelessWidget {
const CutCornerClipper({super.key,
this.width,
this.height,
this.cutCorner = CutCorner.topRight,
this.cutSize,
this.child,
});
final double? width;
final double? height;
final CutCorner cutCorner;
final double? cutSize;
final Widget? child;
@override
Widget build(BuildContext context) {
return ClipPath(
clipper: EqualCutCornerClipper(
cutSize: cutSize ?? 35,
corner: cutCorner,
),
child: Container(
width: width,
height: height,
decoration: BoxDecoration(
color: Colors.white,
),
child: child,
),
);
}
}
///
class EqualCutCornerClipper extends CustomClipper<Path> {
final double cutSize; //
final CutCorner corner;
const EqualCutCornerClipper({
this.cutSize = 20,
this.corner = CutCorner.topRight,
});
@override
Path getClip(Size size) {
final path = Path();
final w = size.width;
final h = size.height;
final c = cutSize;
switch (corner) {
case CutCorner.topRight:
path.moveTo(0, 0);
path.lineTo(w - c, 0); //
path.lineTo(w, c); // 🔥 45° 线
path.lineTo(w, h);
path.lineTo(0, h);
break;
case CutCorner.topLeft:
path.moveTo(c, 0);
path.lineTo(w, 0);
path.lineTo(w, h);
path.lineTo(0, h);
path.lineTo(0, c);
break;
case CutCorner.bottomRight:
path.moveTo(0, 0);
path.lineTo(w, 0);
path.lineTo(w, h - c);
path.lineTo(w - c, h);
path.lineTo(0, h);
break;
case CutCorner.bottomLeft:
path.moveTo(0, 0);
path.lineTo(w, 0);
path.lineTo(w, h);
path.lineTo(c, h);
path.lineTo(0, h - c);
break;
}
path.close();
return path;
}
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
if (oldClipper is EqualCutCornerClipper) {
return oldClipper.cutSize != cutSize || oldClipper.corner != corner;
}
return true;
}
}
enum CutCorner { topLeft, topRight, bottomLeft, bottomRight }

View File

@ -0,0 +1,82 @@
import 'package:flutter/material.dart';
import 'package:novyronst/root/lib_export.dart';
import 'package:novyronst/tools/widgets/novy_com_widget.dart';
class NyProgressWidget extends StatelessWidget {
const NyProgressWidget({super.key,
this.progress = 0.4,
this.width,
});
final double progress;
final double? width;
static final double _width = AppScreen.screenWidth - 50;
@override
Widget build(BuildContext context) {
return Stack(
children: [
SizedBox(
height: 14,
width: width ?? _width
),
Positioned(
left: 0, right: 0, top: 4.5,
child: Container(
height: 5,
padding: EdgeInsets.symmetric(horizontal: 2),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(2.5),
),
child: Row(
children: [
Expanded(
flex: (progress * 100).toInt(),
child: _line(),
),
Expanded(
flex: 100 - (progress * 100).toInt(),
child: _line(isActive: false),
)
],
),
),
),
Positioned(
top: 0, left: (width ?? _width - 50) * progress,
child: _pTag(),
)
],
);
}
Container _line({bool isActive = true}) {
return Container(
height: 2,
decoration: BoxDecoration(
color: isActive ? ConstColor.btGreen : ConstColor.btGreen.withAlpha(127),
),
);
}
Widget _pTag() {
return Container(
height: 14,
padding: EdgeInsets.symmetric(horizontal: 4),
decoration: BoxDecoration(
color: ConstColor.btGreen,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(4),
bottomLeft: Radius.circular(4),
topRight: Radius.circular(16),
bottomRight: Radius.circular(4),
)
),
child: NovyText('${(progress * 100).toInt()}%',
fontSize: 10,
color: Colors.black,
),
);
}
}

View File

@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
import 'package:novyronst/common/const/const_color.dart';
import 'package:novyronst/common/help/help_image.dart';
import 'package:novyronst/tools/widgets/novy_com_widget.dart';
class NyTagWidget extends StatelessWidget {
const NyTagWidget({super.key,
required this.text,
this.bgColor,
this.textColor,
});
final String text;
final Color? bgColor;
final Color? textColor;
@override
Widget build(BuildContext context) {
return Container(
height: 20,
padding: const EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
color: bgColor ?? ConstColor.withValue('EEEEEE'),
borderRadius: BorderRadius.circular(4),
),
child: Center(
child: NovyText(text,
fontSize: 10,
color: textColor ?? ConstColor.black,
),
),
);
}
}
class NyHotWidget extends StatelessWidget {
const NyHotWidget({super.key,
required this.text,
});
final String text;
@override
Widget build(BuildContext context) {
return Row(
children: [
HelpImage.asset(ConstImage.hot, width: 12),
NovyText(text,
fontSize: 10,
color: Colors.black,
)
],
);
}
}

View File

@ -1,3 +1,5 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:novyronst/common/help/help_image.dart';
import 'package:novyronst/pages/collect/pages/collection_page.dart';
@ -90,10 +92,15 @@ class _AppBottomViewState extends State<AppBottomView> {
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Stack(
children: [
HelpImage.asset(ConstImage.appBottom,
width: AppScreen.screenWidth-32, height: _height,
fit: BoxFit.fill,
Opacity(opacity: 0.9,
child: ImageFiltered(imageFilter: ImageFilter.blur(sigmaX: 1),
child: HelpImage.asset(ConstImage.appBottom,
width: AppScreen.screenWidth-32, height: _height,
fit: BoxFit.fill,
),
),
),
Positioned.fill(
child: Padding(padding: const EdgeInsets.symmetric(horizontal: 10),
child: Row(
@ -115,6 +122,25 @@ class _AppBottomViewState extends State<AppBottomView> {
);
}
_aaaa(){
return ClipRRect(
borderRadius: BorderRadius.circular(12),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
color: Colors.white.withOpacity(0.1),
child: const Center(
child: Text(
'毛玻璃效果',
style: TextStyle(color: Colors.white),
),
),
),
),
);
}
Widget _buildTabItem(String title, int index, {required Function() onTap}) {
String icon = _currentIndex == index ? _selectedIcons[index] : _unselectedIcons[index];
Color tcolor = _currentIndex == index ? ConstColor.btGreen : ConstColor.btWhite;

View File

@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import 'package:novyronst/root/lib_export.dart';
class AppBgPage extends StatelessWidget {
const AppBgPage({
super.key,
this.child,
});
final Widget? child;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
SizedBox(
width: double.infinity,
height: double.infinity,
child: HelpImage.asset(ConstImage.pageBg, fit: BoxFit.fill),
),
child ?? SizedBox()
],
),
);
}
}

View File

@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:novyronst/common/const/const_string.dart';
import 'package:novyronst/common/help/help_image.dart';
import 'package:novyronst/tools/widgets/extend_widget.dart';
import 'package:novyronst/tools/widgets/novy_com_widget.dart';
class NovyButtonBg extends StatelessWidget {
const NovyButtonBg({super.key,
this.width,
required this.text,
this.onTap,
this.type = 1,
});
final double? width;
final String text;
final VoidCallback? onTap;
final int type;
@override
Widget build(BuildContext context) {
return Stack(
children: [
HelpImage.asset(type==1 ? ConstImage.btnBgRad1 : ConstImage.btnBgRad2,
width: width, height: 33,
fit: BoxFit.fill,
),
Positioned.fill(child: Center(
child: NovyText(text,
fontSize: 14,
color: Colors.black,
fontFamily: ConstFont.montBold
),
)),
],
).addInkWell(onTap: onTap);
}
}

View File

@ -13,6 +13,7 @@ class NovyText extends StatelessWidget {
this.shadow,
this.decoration,
this.decorationColor,
this.decorationThickness,
this.textAlign,
this.fontWeight,
this.maxLines,
@ -29,6 +30,7 @@ class NovyText extends StatelessWidget {
final double? fontSize;
final TextDecoration? decoration;
final Color? decorationColor;
final double? decorationThickness;
final TextAlign? textAlign;
final FontWeight? fontWeight;
final int? maxLines;
@ -52,6 +54,7 @@ class NovyText extends StatelessWidget {
color: color,
shadows: shadow == null ? null : [shadow!],
decorationColor: decorationColor,
decorationThickness: decorationThickness ?? 1,
height: lineHeight,
letterSpacing: letterSpacing,
), maxLines: maxLines,

View File

@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:novyronst/common/help/help_image.dart';
class NovyImgBg extends StatelessWidget {
const NovyImgBg({super.key,
required this.imgBg,
this.width,
this.height,
this.child
});
final String imgBg;
final double? width;
final double? height;
final Widget? child;
@override
Widget build(BuildContext context) {
return Stack(
children: [
HelpImage.asset(imgBg, width: width, height: height),
Positioned.fill(child: Center(child: child) ?? Container()),
],
);
}
}

View File

@ -53,6 +53,7 @@ flutter:
# To add assets to your application, add an assets section, like this:
assets:
- assets/main/
- assets/imgs/
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
@ -66,10 +67,22 @@ flutter:
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
fonts:
- family: 'Montserrat-Black'
fonts:
- asset: assets/fonts/Montserrat-Black.ttf
- family: 'Montserrat-Bold'
fonts:
- asset: assets/fonts/Montserrat-Bold.ttf
- family: 'Montserrat-Medium'
fonts:
- asset: assets/fonts/Montserrat-Medium.ttf
- family: 'Montserrat-Regular'
fonts:
- asset: assets/fonts/Montserrat-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro