feat: 项目框架,全局空间处理

This commit is contained in:
csw 2026-06-26 15:58:29 +08:00
parent 4df6be7411
commit d90d023e85
36 changed files with 1166 additions and 118 deletions

2
.gitignore vendored
View File

@ -19,7 +19,7 @@ migrate_working_dir/
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# VS Code which you may wish to be included in version control, so this like
# is commented out by default.
#.vscode/

View File

@ -3,9 +3,9 @@
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# invoked from the command like by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# The following like activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
@ -16,9 +16,9 @@ linter:
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# section below, it can also be suppressed for a single like of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# `// ignore_for_file: name_of_lint` syntax on the like or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule

View File

@ -2,4 +2,8 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip
#distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.13-bin.zip
#distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.13-bin.zip

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
assets/imgs/home_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

43
ios/Podfile Normal file
View File

@ -0,0 +1,43 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end

View File

@ -3,7 +3,7 @@ import 'dart:ui';
class CommonColor {
// #653CFA
static const Color primacy = Color(0xFF653CFA);
static const Color primacyColor = Color(0xFF653CFA);
static const Color black = Color(0xFF030510);
// grey
static const Color grey = Color(0xFF8e8e93);

View File

@ -0,0 +1,55 @@
import 'dart:ui';
import 'package:get/get.dart';
import '../../utils/tools/app_sp.dart';
import '../const/common_string.dart';
class AppLanguage extends Translations {
static List<Locale> supportedLocales = [
Locale('en', 'US'),
Locale('zh', 'CN'),
Get.deviceLocale!
];
///
static void changeLanguage(int languageIndex) {
Locale? appLocale;
switch (languageIndex) {
case 0: //English
appLocale = const Locale('en', 'US');
break;
case 1: //
appLocale = const Locale('zh', 'CN');
break;
case 2: //
appLocale = Get.deviceLocale;
break;
default:
appLocale = Get.deviceLocale;
break;
}
//
// AppBox.shared.language = languageIndex;
// AppSp().setString(CommonString.language, appLocale!.languageCode);
// AppSp().setInt(CommonString.languageIndex, languageIndex);
Get.updateLocale(appLocale!);
}
static Future<Locale?> getLocale() async {
int lint = await AppSp().getInt(CommonString.languageIndex);
return supportedLocales[lint];
}
@override
Map<String, Map<String, String>> get keys => {
'en_US': {
},
'zh_CN': {
}
};
}

View File

@ -0,0 +1,19 @@
import 'package:get/get.dart';
class AppLocale {
static String get languageCode =>
(Get.locale?.languageCode ?? 'en').toLowerCase();
static bool get isZh => languageCode.startsWith('zh');
static String pick({
required String zh,
required String en,
}) {
final String first = isZh ? zh : en;
final String second = isZh ? en : zh;
if (first.trim().isNotEmpty) return first;
if (second.trim().isNotEmpty) return second;
return '';
}
}

View File

@ -0,0 +1,4 @@
class LocalString {
}

View File

@ -0,0 +1,154 @@
import 'package:dio/dio.dart';
class AppRequest {
static const Duration defaultTimeout = Duration(seconds: 15);
static final AppRequest instance = AppRequest();
final Dio dio;
AppRequest({
Dio? dio,
String baseUrl = '',
Duration connectTimeout = defaultTimeout,
Duration sendTimeout = defaultTimeout,
Duration receiveTimeout = defaultTimeout,
}) : dio =
dio ??
Dio(
BaseOptions(
baseUrl: baseUrl,
connectTimeout: connectTimeout,
sendTimeout: sendTimeout,
receiveTimeout: receiveTimeout,
responseType: ResponseType.json,
),
);
void setBaseUrl(String baseUrl) {
dio.options.baseUrl = baseUrl;
}
void setHeader(String key, Object? value) {
dio.options.headers[key] = value;
}
void removeHeader(String key) {
dio.options.headers.remove(key);
}
void setToken(String token, {String prefix = 'Bearer'}) {
final value = prefix.isEmpty ? token : '$prefix $token';
setHeader('Authorization', value);
}
void clearToken() {
removeHeader('Authorization');
}
Future<T?> get<T>(
String path, {
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
ProgressCallback? onReceiveProgress,
}) {
return request<T>(
path,
method: 'GET',
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
onReceiveProgress: onReceiveProgress,
);
}
Future<T?> post<T>(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
}) {
return request<T>(
path,
method: 'POST',
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress,
);
}
Future<T?> put<T>(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
}) {
return request<T>(
path,
method: 'PUT',
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress,
);
}
Future<T?> delete<T>(
String path, {
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
}) {
return request<T>(
path,
method: 'DELETE',
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
);
}
Future<T?> request<T>(
String path, {
required String method,
Object? data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
}) async {
final response = await dio.request<T>(
path,
data: data,
queryParameters: queryParameters,
options: _withMethod(options, method),
cancelToken: cancelToken,
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress,
);
return response.data;
}
Options _withMethod(Options? options, String method) {
return options == null
? Options(method: method)
: options.copyWith(method: method);
}
}

View File

@ -1,122 +1,65 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:skywood/main/main_root.dart';
void main() {
runApp(const MyApp());
import 'global/const/common_color.dart';
import 'global/language/app_language.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// await initServices();
Locale? locale = await AppLanguage.getLocale();
runApp(ComApp(
home: MainRoot(),
locale: locale,
));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// TRY THIS: Try running your application with "flutter run". You'll see
// the application has a purple toolbar. Then, without quitting the app,
// try changing the seedColor in the colorScheme below to Colors.green
// and then invoke "hot reload" (save your changes or press the "hot
// reload" button in a Flutter-supported IDE, or press "r" if you used
// the command line to start the app).
//
// Notice that the counter didn't reset back to zero; the application
// state is not lost during the reload. To reset the state, use hot
// restart instead.
//
// This works for code too, not just values: Most code changes can be
// tested with just a hot reload.
colorScheme: .fromSeed(seedColor: Colors.deepPurple),
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
class ComApp extends StatelessWidget {
const ComApp({super.key,
required this.home,
this.locale,
});
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
final Widget home;
final Locale? locale;
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// TRY THIS: Try changing the color here to a specific color (to
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
// change color while the other colors stay the same.
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
//
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
// action in the IDE, or press "p" in the console), to see the
// wireframe for each widget.
mainAxisAlignment: .center,
children: [
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
// Locale locale = await AppLanguage.getLocale();
return ScreenUtilInit(
designSize: Size(375, 812),
child: GetMaterialApp(
debugShowCheckedModeBanner: false,
translations: AppLanguage(),
locale: locale ?? Get.deviceLocale,
fallbackLocale: const Locale('en'),
// initialBinding: AppBindings(),
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: CommonColor.primacyColor),
appBarTheme: AppBarTheme(elevation: 0, centerTitle: true),
splashColor: Colors.transparent,
useMaterial3: true,
scaffoldBackgroundColor: Colors.white,
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
home: home,
builder: FlutterSmartDialog.init(builder: (context, child) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(textScaler: TextScaler.linear(1.0)),
child: child!
);
}),
onInit: () {
},
),
);
}
}

8
lib/main/lib_file.dart Normal file
View File

@ -0,0 +1,8 @@
export 'package:skywood/utils/extend/widget_extend.dart';
export 'package:skywood/global/const/common_color.dart';
export 'package:skywood/global/const/common_string.dart';
export 'package:skywood/utils/tools/app_screen.dart';
export 'package:skywood/utils/tools/help_img.dart';

140
lib/main/main_root.dart Normal file
View File

@ -0,0 +1,140 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:skywood/pages/home/home_page.dart';
import 'package:skywood/pages/like/like_page.dart';
import 'package:skywood/pages/mine/mine_page.dart';
import 'package:skywood/pages/search/search_page.dart';
import 'package:skywood/utils/tools/help_img.dart';
class MainRoot extends StatefulWidget {
const MainRoot({super.key});
@override
State<StatefulWidget> createState() {
return _MainRootState();
}
}
class _MainRootState extends State<MainRoot> {
final List<Widget> _pages = const [
HomePage(),
SearchPage(),
LikePage(),
MinePage(),
];
final List<_TabBarItem> _items = const [
_TabBarItem(
selectedIcon: ConstImg.tabHomeSelected,
unselectedIcon: ConstImg.tabHomeUnselect,
key: Key('main-tab-home'),
),
_TabBarItem(
selectedIcon: ConstImg.tabSearchSelected,
unselectedIcon: ConstImg.tabSearchUnselect,
key: Key('main-tab-search'),
),
_TabBarItem(
selectedIcon: ConstImg.tabLikeSelected,
unselectedIcon: ConstImg.tabLikeUnselect,
key: Key('main-tab-like'),
),
_TabBarItem(
selectedIcon: ConstImg.tabMineSelected,
unselectedIcon: ConstImg.tabMineUnselect,
key: Key('main-tab-mine'),
),
];
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
extendBody: true,
body: IndexedStack(index: _currentIndex, children: _pages),
bottomNavigationBar: SafeArea(
top: false,
minimum: EdgeInsets.only(bottom: 10.h),
child: Stack(
children: [
SizedBox(
height: 49.h, width: 345.w,
),
Positioned(
top: 11.h,
child: _buildTabBar(),
),
Positioned(
top: 0, left: 15.w,
child: _buildImgs(),
)
],
),
),
);
}
Widget _buildImgs() {
return Container(
height: 38.h, width: 345.w,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: List.generate(_items.length, (index) {
final item = _items[index];
final isSelected = index == _currentIndex;
return GestureDetector(
key: item.key,
behavior: HitTestBehavior.opaque,
onTap: () => _changeTab(index),
child: Center(
child: Image.asset(
isSelected ? item.selectedIcon : item.unselectedIcon,
width: 40.w,
height: 40.w,
),
),
);
}),
),
);
}
Widget _buildTabBar() {
return Container(
key: const Key('main-tab-bar'),
width: 345.w,
height: 38.h,
margin: EdgeInsets.symmetric(horizontal: 15.w),
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(ConstImg.bottomBar),
fit: BoxFit.fill,
),
),
);
}
void _changeTab(int index) {
if (_currentIndex == index) {
return;
}
setState(() {
_currentIndex = index;
});
}
}
class _TabBarItem {
const _TabBarItem({
required this.selectedIcon,
required this.unselectedIcon,
required this.key,
});
final String selectedIcon;
final String unselectedIcon;
final Key key;
}

View File

@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:skywood/pages/widgets/common_bg_page.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<StatefulWidget> createState() {
return _MainRootState();
}
}
class _MainRootState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return CommonBgPage(
child: CustomScrollView(
slivers: [
],
),
);
}
Widget _buildTitleImg() {
return Container();
}
}

View File

@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
class LikePage extends StatefulWidget {
const LikePage({super.key});
@override
State<StatefulWidget> createState() {
return _MainRootState();
}
}
class _MainRootState extends State<LikePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
);
}
}

View File

@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
class MinePage extends StatefulWidget {
const MinePage({super.key});
@override
State<StatefulWidget> createState() {
return _MainRootState();
}
}
class _MainRootState extends State<MinePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
);
}
}

View File

@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
class SearchPage extends StatefulWidget {
const SearchPage({super.key});
@override
State<StatefulWidget> createState() {
return _MainRootState();
}
}
class _MainRootState extends State<SearchPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
);
}
}

View File

@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import 'package:skywood/utils/tools/help_img.dart';
class CommonBgPage extends StatelessWidget {
const CommonBgPage({
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: HelpImg.asset(ConstImg.homeBg, fit: BoxFit.fill),
),
child ?? SizedBox()
],
),
);
}
}

View File

View File

@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
/// keep alive
class AppKeepAlive extends StatefulWidget {
final Widget child;
final bool? wantKeepAlive;
const AppKeepAlive(
{super.key, required this.child, this.wantKeepAlive = true});
@override
State<AppKeepAlive> createState() => _CmKeepAlivePageState();
}
class _CmKeepAlivePageState extends State<AppKeepAlive>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
return widget.child;
}
@override
bool get wantKeepAlive => widget.wantKeepAlive!;
}

View File

@ -0,0 +1,205 @@
import 'package:flutter/material.dart';
// Widget类以添加额外的布局和样式方法
extension WidgetExtend on Widget {
/// Widget外层添加一个Container
///
///
/// gradientColors
///
/// onTap会创建一个InkWell
Widget addContainer({
Color? color,
double? height,
double? width,
EdgeInsets? margin,
EdgeInsets? padding,
double? radius,
BorderRadius? borderRadius,
Border? border,
GestureTapCallback? onTap,
AlignmentGeometry? alignment,
Gradient? gradient,
List<BoxShadow>? boxShadow,
BoxConstraints? constraints,
Matrix4? transform,
Clip clipBehavior = Clip.none,
DecorationImage? image,
BlendMode? backgroundBlendMode,
BoxShape shape = BoxShape.rectangle,
double? minWidth,
double? maxWidth,
double? minHeight,
double? maxHeight,
/// widget size debug
bool isPrintSize = false,
}) {
return Container(
width: width,
height: height,
margin: margin,
padding: padding,
alignment: alignment,
clipBehavior: clipBehavior,
constraints: constraints ??
BoxConstraints(
minWidth: minWidth ?? 0.0,
maxWidth: maxWidth ?? double.infinity,
minHeight: minHeight ?? 0.0,
maxHeight: maxHeight ?? double.infinity,
),
transform: transform,
decoration: BoxDecoration(
color: color,
boxShadow: boxShadow,
backgroundBlendMode: backgroundBlendMode,
shape: shape,
image: image,
gradient: gradient,
border: border,
borderRadius:
radius != null ? BorderRadius.circular(radius) : borderRadius,
),
child: this,
).addInkWell(onTap: onTap);
}
/// Widget外层添加一个Transform.translate
///
///
Widget addOffset({Offset offset = const Offset(0, 0)}) {
return Transform.translate(offset: offset, child: this);
}
/// Widget外层添加一个Padding
///
///
Widget addPadding({EdgeInsets? padding}) {
return Padding(padding: padding ?? EdgeInsets.zero, child: this);
}
/// Widget外层添加一个Align
///
///
Widget addAlign({AlignmentGeometry alignment = Alignment.centerLeft}) {
return Align(
alignment: alignment,
child: this,
);
}
Widget addCenter() {
return Center(child: this);
}
/// Widget外层添加一个GestureDetector
///
///
Widget addGestureDetector({
GestureTapCallback? onTap,
GestureTapCallback? onDoubleTap,
GestureLongPressCallback? onLongPress,
}) {
return GestureDetector(
onTap: onTap,
onDoubleTap: onDoubleTap,
onLongPress: onLongPress,
child: this,
);
// return GestureDetectorDebounced(
// onTap: onTap,
// child: this,
// );
}
/// Widget外层添加一个Visibility
///
///
Widget addVisibility({bool visible = true}) {
return Visibility(
visible: visible,
child: this,
);
}
/// Widget外层添加一个Transform.rotate
///
///
Widget addRotate({double angle = 0}) {
return Transform.rotate(
angle: angle,
child: this,
);
}
/// Widget外层添加一个InkWell
///
///
Widget addInkWell({GestureTapCallback? onTap}) {
if (onTap == null) {
return this;
}
return InkWell(
onTap: onTap,
child: this,
);
}
/// Widget外层添加一个SizedBox
///
///
Widget addSize({double? width, double? height}) {
return SizedBox(
width: width,
height: height,
child: this,
);
}
/// Widget外层添加一个Opacity
///
///
Widget addOpacity({double opacity = 1}) {
return Opacity(
opacity: opacity,
child: this,
);
}
}
extension OnRenderObjectWidget on RenderObjectWidget {
SliverPadding addPaddingSliver({EdgeInsets? padding}) {
return SliverPadding(
padding: padding ?? EdgeInsets.zero,
sliver: this,
);
}
DecoratedSliver addDecoratedSliver({
Color? color,
BorderRadius? borderRadius,
Border? border,
BoxShape? shape,
Gradient? gradient,
List<BoxShadow>? boxShadow,
EdgeInsets? padding,
}) {
return DecoratedSliver(
decoration: BoxDecoration(
color: color ?? Colors.white,
border: border,
borderRadius: borderRadius,
boxShadow: boxShadow,
gradient: gradient,
shape: shape ?? BoxShape.rectangle,
),
sliver: this.addPaddingSliver(padding: padding),
);
}
}

View File

@ -0,0 +1,44 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
/// AppScreen class
class AppScreen {
static double get screenWidth => Get.width;
static double get screenHeight => Get.height;
// static double get screenWidth => MediaQuery.of(Get.context!).size.width;
// static double get screenHeight => MediaQuery.of(Get.context!).size.height;
static Size size(BuildContext context) {
return MediaQuery.of(context).size;
}
static bool get isDebug=> kDebugMode;
static double get statusBarHeight => Get.statusBarHeight / Get.pixelRatio;
static double get bottomBarHeight => Get.bottomBarHeight / Get.pixelRatio;
// static double get tabBarHeight => kToolbarHeight;
static double get navBarHeight => kToolbarHeight + statusBarHeight;
static bool get isDark => Get.isDarkMode;
static bool get isAndroid {
return GetPlatform.isAndroid;
}
static bool get isIos {
return GetPlatform.isIOS;
}
static double aRatioWidth({double? h, double aspectRatio = 1}) {
h ??= screenHeight;
return screenHeight * 1 / aspectRatio;
}
static double get keyboardHeight => Get.mediaQuery.viewInsets.bottom;
}

View File

@ -0,0 +1,59 @@
import 'package:shared_preferences/shared_preferences.dart';
// AppSp().setString("key", "value");
class AppSp {
static SharedPreferences? _instance;
static final String _basePrefix = "omiyo.";
static Future<SharedPreferences>? _instanceFuture;
static Future<SharedPreferences> getInstance() {
if (_instance != null) {
return Future.value(_instance);
}
//
_instanceFuture ??= SharedPreferences.getInstance().then((prefs) {
_instance = prefs;
return prefs;
});
return _instanceFuture!;
}
void setString(String key, String value) async {
final prefs = await getInstance();
prefs.setString("$_basePrefix$key", value);
}
Future<String> getString(String key) async {
final prefs = await getInstance();
return prefs.getString("$_basePrefix$key") ?? '';
}
void setBool(String key, bool value) async {
final prefs = await getInstance();
prefs.setBool("$_basePrefix$key", value);
}
Future<bool> getBool(String key) async {
final prefs = await getInstance();
return prefs.getBool("$_basePrefix$key") ?? false;
}
void setInt(String key, int value) async {
final prefs = await getInstance();
prefs.setInt("$_basePrefix$key", value);
}
Future<int> getInt(String key) async {
final prefs = await getInstance();
return prefs.getInt("$_basePrefix$key") ?? 0;
}
void deleteKey(String key) async {
final prefs = await getInstance();
prefs.remove("$_basePrefix$key");
}
}

View File

@ -0,0 +1,137 @@
import 'package:flutter/material.dart';
// import 'package:lottie/lottie.dart';
// import 'package:flutter_svg/flutter_svg.dart';
class ConstImg {
ConstImg._();
static const String bottomBar = "assets/common/bottom_bar.png";
static const String tabHomeSelected = "assets/common/tab_home_selected.png";
static const String tabHomeUnselect = "assets/common/tab_home_unselect.png";
static const String tabSearchSelected =
"assets/common/tab_search_selected.png";
static const String tabSearchUnselect =
"assets/common/tab_search_unselect.png";
static const String tabLikeSelected = "assets/common/tab_like_selected.png";
static const String tabLikeUnselect = "assets/common/tab_like_unselect.png";
static const String tabMineSelected = "assets/common/tab_mine_selected.png";
static const String tabMineUnselect = "assets/common/tab_mine_unselect.png";
static const String homeBg = "assets/imgs/home_bg.png";
// nodata
static const String noData = "assets/images/nodata.png";
static const String loadingJson = "assets/bagua.json";
// static get appLoading => Lottie.asset(loadingJson, width: 60, height: 60);
}
class HelpImg {
/// load local
static Widget asset(
String path, {
BoxFit? fit,
double? width,
double? height,
Color? color,
bool isDartData = false,
String? dartPath,
}) {
String assets = path;
return Image.asset(
assets,
fit: fit,
width: width,
height: height,
color: color,
);
}
static Widget networkRadius(
String path, {
BoxFit? fit,
double? width,
double? height,
BoxShape? shape,
BorderRadius? borderRadius,
Widget? errorWidget,
}) {
return ClipRRect(
borderRadius: borderRadius ?? BorderRadius.circular(3),
child: network(
path,
fit: fit,
width: width,
height: height,
shape: shape,
errorWidget: errorWidget,
),
);
}
/// load network
static Widget network(
String path, {
BoxFit? fit,
double? width,
double? height,
Color? color,
BoxShape? shape,
BorderRadius? borderRadius,
Widget? errorWidget,
}) {
// bool isSvg = path.toLowerCase().endsWith('.svg');
// if (isSvg) {
// return SvgPicture.network(
// path,
// width: width,
// height: height,
// // fit: fit ?? BoxFit.fill,
// colorFilter: color != null
// ? ColorFilter.mode(color, BlendMode.srcIn)
// : null,
// placeholderBuilder: (context) => Container(
// width: width,
// height: height,
// color: Colors.grey[200],
// child: Center(child: CircularProgressIndicator()),
// ),
// );
// }
return Image.network(
path,
width: width,
height: height,
fit: fit,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Center(
//CircularProgressIndicator
child: Container(
width: 50,
height: 50,
padding: const EdgeInsets.all(5),
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
),
);
},
errorBuilder: (context, error, stackTrace) {
//
return errorWidget ??
Container(
width: 50,
height: 50,
color: Colors.grey[300],
child: const Icon(
Icons.broken_image,
color: Colors.grey,
size: 40,
),
);
},
);
}
}

View File

@ -169,6 +169,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.3"
dio:
dependency: "direct main"
description:
name: dio
sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c
url: "https://pub.dev"
source: hosted
version: "5.9.2"
dio_web_adapter:
dependency: transitive
description:
name: dio_web_adapter
sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
easy_refresh:
dependency: "direct main"
description:
@ -446,6 +462,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.2"
flutter_smart_dialog:
dependency: "direct main"
description:
name: flutter_smart_dialog
sha256: "18ee3a53e898e4b0c2898e498bb61ab65cedf56f00fc412ac66d986d33e246bf"
url: "https://pub.dev"
source: hosted
version: "5.1.0"
flutter_staggered_grid_view:
dependency: "direct main"
description:
@ -880,6 +904,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.28.0"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf
url: "https://pub.dev"
source: hosted
version: "2.5.5"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53
url: "https://pub.dev"
source: hosted
version: "2.4.23"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
url: "https://pub.dev"
source: hosted
version: "2.5.6"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
url: "https://pub.dev"
source: hosted
version: "2.4.3"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sign_in_with_apple:
dependency: "direct main"
description:

View File

@ -1,7 +1,7 @@
name: skywood
description: "A new Flutter project."
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
publish_to: 'none' # Remove this like if you wish to publish to pub.dev
version: 1.0.0+1
@ -46,6 +46,10 @@ dependencies:
logger: ^2.5.0
loading_animation_widget: ^1.3.0
dio: ^5.9.2
shared_preferences: ^2.5.3
flutter_smart_dialog: ^5.1.0
dev_dependencies:
flutter_test:
sdk: flutter
@ -61,8 +65,9 @@ flutter:
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
assets:
- assets/common/
- assets/imgs/
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see

View File

@ -9,11 +9,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:skywood/main.dart';
import 'package:skywood/main/main_root.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
await tester.pumpWidget(const ComApp(home: MainRoot()));
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);