Commit code. Update time: 2023-06-25
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
import 'dart:core' as core;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
void print (core.Object object) {
|
||||
if (kDebugMode) {
|
||||
core.print(object);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:convert/convert.dart';
|
||||
|
||||
/// Encrypt Util.
|
||||
class EncryptUtil {
|
||||
/// md5 加密
|
||||
static String encodeMd5(String data) {
|
||||
var content = Utf8Encoder().convert(data);
|
||||
var digest = md5.convert(content);
|
||||
return hex.encode(digest.bytes);
|
||||
}
|
||||
|
||||
/// 异或对称加密
|
||||
static String xorCode(String res, String key) {
|
||||
List<String> keyList = key.split(',');
|
||||
List<int> codeUnits = res.codeUnits;
|
||||
List<int> codes = [];
|
||||
for (int i = 0, length = codeUnits.length; i < length; i++) {
|
||||
int code = codeUnits[i] ^ int.parse(keyList[i % keyList.length]);
|
||||
codes.add(code);
|
||||
}
|
||||
return String.fromCharCodes(codes);
|
||||
}
|
||||
|
||||
/// 异或对称 Base64 加密
|
||||
static String xorBase64Encode(String res, String key) {
|
||||
String encode = xorCode(res, key);
|
||||
encode = encodeBase64(encode);
|
||||
return encode;
|
||||
}
|
||||
|
||||
/// 异或对称 Base64 解密
|
||||
static String xorBase64Decode(String res, String key) {
|
||||
String encode = decodeBase64(res);
|
||||
encode = xorCode(encode, key);
|
||||
return encode;
|
||||
}
|
||||
|
||||
/// Base64加密
|
||||
static String encodeBase64(String data) {
|
||||
var content = utf8.encode(data);
|
||||
var digest = base64Encode(content);
|
||||
return digest;
|
||||
}
|
||||
|
||||
/// Base64解密
|
||||
static String decodeBase64(String data) {
|
||||
List<int> bytes = base64Decode(data);
|
||||
String result = utf8.decode(bytes);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:sail/constant/app_strings.dart';
|
||||
import 'package:sail/router/application.dart';
|
||||
import 'package:sail/router/routers.dart';
|
||||
import 'package:sail/utils/common_util.dart';
|
||||
import 'package:sail/utils/shared_preferences_util.dart';
|
||||
|
||||
class HttpUtil {
|
||||
static HttpUtil get instance => _httpUtil;
|
||||
static final HttpUtil _httpUtil = HttpUtil();
|
||||
late Dio dio;
|
||||
|
||||
HttpUtil() {
|
||||
BaseOptions options = BaseOptions(
|
||||
connectTimeout: 10000,
|
||||
receiveTimeout: 10000,
|
||||
);
|
||||
dio = Dio(options);
|
||||
dio.interceptors
|
||||
.add(InterceptorsWrapper(onRequest: (options, handler) async {
|
||||
//print("========================请求数据===================");
|
||||
//print("url=${options.uri.toString()}");
|
||||
//print("headers=${options.headers}");
|
||||
//print("params=${options.data}");
|
||||
|
||||
//如果token存在在请求参数加上token
|
||||
await SharedPreferencesUtil.getInstance()
|
||||
?.getString(AppStrings.token)
|
||||
.then((token) {
|
||||
if (token != null) {
|
||||
options.queryParameters[AppStrings.token] = token;
|
||||
//print("token=$token");
|
||||
}
|
||||
});
|
||||
|
||||
//如果auth_data存在在请求参数加上auth_data
|
||||
await SharedPreferencesUtil.getInstance()
|
||||
?.getString(AppStrings.authData)
|
||||
.then((authData) {
|
||||
if (authData != null) {
|
||||
options.queryParameters[AppStrings.authData] = authData;
|
||||
//print("authData=$authData");
|
||||
}
|
||||
});
|
||||
|
||||
return handler.next(options);
|
||||
}, onResponse: (response, handler) {
|
||||
//print("========================请求数据===================");
|
||||
//print("code=${response.statusCode}");
|
||||
|
||||
if (response.statusCode! < 200 || response.statusCode! >= 300) {
|
||||
if (response.statusCode == 403) {
|
||||
Application.navigatorKey.currentState?.pushNamed(Routers.login);
|
||||
}
|
||||
|
||||
return handler.reject(DioError(
|
||||
requestOptions: response.requestOptions,
|
||||
response: response,
|
||||
type: DioErrorType.response));
|
||||
}
|
||||
|
||||
return handler.next(response);
|
||||
}, onError: (error, handler) {
|
||||
//print("========================请求错误===================");
|
||||
//print("message =${error.message}");
|
||||
//print("code=${error.response?.statusCode}");
|
||||
|
||||
return handler.next(error);
|
||||
}));
|
||||
}
|
||||
|
||||
//get请求
|
||||
Future get(String url,
|
||||
{Map<String, dynamic>? parameters, Options? options}) async {
|
||||
Response response;
|
||||
if (parameters != null && options != null) {
|
||||
response =
|
||||
await dio.get(url, queryParameters: parameters, options: options);
|
||||
} else if (parameters != null && options == null) {
|
||||
response = await dio.get(url, queryParameters: parameters);
|
||||
} else if (parameters == null && options != null) {
|
||||
response = await dio.get(url, options: options);
|
||||
} else {
|
||||
response = await dio.get(url);
|
||||
}
|
||||
return response.data;
|
||||
}
|
||||
|
||||
//post请求
|
||||
Future post(String url,
|
||||
{Map<String, dynamic>? parameters, Options? options}) async {
|
||||
Response response;
|
||||
if (parameters != null && options != null) {
|
||||
response = await dio.post(url, data: parameters, options: options);
|
||||
} else if (parameters != null && options == null) {
|
||||
response = await dio.post(url, data: parameters);
|
||||
} else if (parameters == null && options != null) {
|
||||
response = await dio.post(url, options: options);
|
||||
} else {
|
||||
response = await dio.post(url);
|
||||
}
|
||||
return response.data;
|
||||
}
|
||||
|
||||
//put请求
|
||||
Future put(String url,
|
||||
{Map<String, dynamic>? parameters, Options? options}) async {
|
||||
Response response;
|
||||
if (parameters != null && options != null) {
|
||||
response = await dio.put(url, data: parameters, options: options);
|
||||
} else if (parameters != null && options == null) {
|
||||
response = await dio.put(url, data: parameters);
|
||||
} else if (parameters == null && options != null) {
|
||||
response = await dio.put(url, options: options);
|
||||
} else {
|
||||
response = await dio.put(url);
|
||||
}
|
||||
return response.data;
|
||||
}
|
||||
|
||||
//delete请求
|
||||
Future delete(String url,
|
||||
{Map<String, dynamic>? parameters, Options? options}) async {
|
||||
Response response;
|
||||
if (parameters != null && options != null) {
|
||||
response = await dio.delete(url, data: parameters, options: options);
|
||||
} else if (parameters != null && options == null) {
|
||||
response = await dio.delete(url, data: parameters);
|
||||
} else if (parameters == null && options != null) {
|
||||
response = await dio.delete(url, options: options);
|
||||
} else {
|
||||
response = await dio.delete(url);
|
||||
}
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
export 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
extension AppLocalizationsX on BuildContext {
|
||||
AppLocalizations get l10n => AppLocalizations.of(this)!;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
||||
class MessageUtil {
|
||||
static toast(String msg) {
|
||||
Fluttertoast.showToast(
|
||||
msg: msg,
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.CENTER,
|
||||
timeInSecForIosWeb: 1,
|
||||
backgroundColor: Colors.black,
|
||||
textColor: Colors.white,
|
||||
fontSize: 16.0);
|
||||
}
|
||||
|
||||
static alert(String msg, BuildContext context, {Function? callback}) {
|
||||
showCupertinoDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: CupertinoAlertDialog(
|
||||
title: Text("提示"),
|
||||
content: Text(msg),
|
||||
actions: <Widget>[
|
||||
CupertinoButton(
|
||||
child: Text("确定"),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
callback!();
|
||||
}),
|
||||
]),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static snack(String msg, BuildContext context) {
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: Text(msg),
|
||||
action: SnackBarAction(
|
||||
label: "关闭",
|
||||
onPressed: () => ScaffoldMessenger.of(context).hideCurrentSnackBar(),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:fluro/fluro.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
import 'package:sail/pages/accountPage.dart';
|
||||
import 'package:sail/pages/crisp_page.dart';
|
||||
import 'package:sail/pages/plan/plan_page.dart';
|
||||
import 'package:sail/pages/server_list.dart';
|
||||
import 'package:sail/router/application.dart';
|
||||
import 'package:sail/router/routers.dart';
|
||||
|
||||
class NavigatorUtil {
|
||||
static goHomePage(BuildContext context) {
|
||||
Application.router?.navigateTo(context, Routers.home,
|
||||
transition: TransitionType.inFromRight, replace: true);
|
||||
}
|
||||
|
||||
static goLogin(BuildContext context) {
|
||||
Application.router?.navigateTo(context, Routers.login,
|
||||
transition: TransitionType.inFromRight, replace: true);
|
||||
}
|
||||
|
||||
static goPlan(BuildContext context) {
|
||||
showCupertinoModalBottomSheet(
|
||||
context: context, builder: (context) => const PlanPage());
|
||||
// Application.router.navigateTo(context, Routers.plan,
|
||||
// transition: TransitionType.inFromRight);
|
||||
}
|
||||
|
||||
static goSettings(BuildContext context) {
|
||||
// showCupertinoModalBottomSheet(
|
||||
// context: context, builder: (context) => const AccountPage());
|
||||
// Application.router.navigateTo(context, Routers.plan,
|
||||
// transition: TransitionType.inFromRight);
|
||||
// Application.router?.navigateTo(context, Routers.account,
|
||||
// transition: TransitionType.inFromRight, replace: true);
|
||||
Navigator.of(context)
|
||||
.push(MaterialPageRoute(builder: (builder) => const AccountPage()));
|
||||
}
|
||||
|
||||
static goServerList(BuildContext context) {
|
||||
// showCupertinoModalBottomSheet(
|
||||
// context: context, builder: (context) => const ServerListPage());
|
||||
|
||||
showCupertinoModalBottomSheet(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return Container(
|
||||
height: MediaQuery.of(context).size.height /
|
||||
1.3, // set height to half of the screen
|
||||
child: Center(
|
||||
child: const ServerListPage(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// Application.router.navigateTo(context, Routers.serverList,
|
||||
// transition: TransitionType.inFromRight);
|
||||
}
|
||||
|
||||
static goToCrisp(BuildContext context) {
|
||||
showCupertinoModalBottomSheet(
|
||||
context: context, builder: (context) => const CrispPage());
|
||||
}
|
||||
|
||||
static goWebView(BuildContext context, String titleName, String url) {
|
||||
String encodeUrl = Uri.encodeComponent(jsonEncode(url));
|
||||
String encodeTitleName = Uri.encodeComponent(jsonEncode(titleName));
|
||||
return Application.router?.navigateTo(
|
||||
context, "${Routers.webView}?titleName=$encodeTitleName&url=$encodeUrl",
|
||||
transition: TransitionType.inFromRight);
|
||||
}
|
||||
|
||||
static goBack(BuildContext context) {
|
||||
Application.router?.pop(context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class SharedPreferencesUtil {
|
||||
SharedPreferencesUtil._();
|
||||
|
||||
static SharedPreferencesUtil? _instance;
|
||||
late SharedPreferences sharedPreferences;
|
||||
|
||||
static SharedPreferencesUtil? getInstance() {
|
||||
_instance ??= SharedPreferencesUtil._();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
static saveData<T>(String key, T value) async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
|
||||
switch (T) {
|
||||
case String:
|
||||
prefs.setString(key, value as String);
|
||||
break;
|
||||
case int:
|
||||
prefs.setInt(key, value as int);
|
||||
break;
|
||||
case bool:
|
||||
prefs.setBool(key, value as bool);
|
||||
break;
|
||||
case double:
|
||||
prefs.setDouble(key, value as double);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<T?> getData<T>(String key) async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
|
||||
T? res;
|
||||
switch (T) {
|
||||
case String:
|
||||
res = prefs.getString(key) as T;
|
||||
break;
|
||||
case int:
|
||||
res = prefs.getInt(key) as T;
|
||||
break;
|
||||
case bool:
|
||||
res = prefs.getBool(key) as T;
|
||||
break;
|
||||
case double:
|
||||
res = prefs.getDouble(key) as T;
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<bool> setBool(String tag, bool isFirst) async {
|
||||
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
|
||||
return sharedPreferences.setBool(tag, isFirst);
|
||||
}
|
||||
|
||||
Future<bool> setString(String tag, String data) async {
|
||||
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
|
||||
return sharedPreferences.setString(tag, data);
|
||||
}
|
||||
|
||||
Future<bool> setMap(String tag, Map<String, dynamic>? data) async {
|
||||
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
|
||||
return sharedPreferences.setString(tag, jsonEncode(data));
|
||||
}
|
||||
|
||||
Future<bool> setList(String tag, List<dynamic>? data) async {
|
||||
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
|
||||
return sharedPreferences.setString(tag, jsonEncode(data));
|
||||
}
|
||||
|
||||
Future<bool?> getBool(String tag) async {
|
||||
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
|
||||
return sharedPreferences.getBool(tag);
|
||||
}
|
||||
|
||||
Future<String?> getString(String tag) async {
|
||||
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
|
||||
return sharedPreferences.getString(tag);
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getMap(String tag) async {
|
||||
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
|
||||
return jsonDecode(sharedPreferences.getString(tag) ?? '{}');
|
||||
}
|
||||
|
||||
Future<List<dynamic>> getList(String tag) async {
|
||||
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
|
||||
return jsonDecode(sharedPreferences.getString(tag) ?? '[]');
|
||||
}
|
||||
|
||||
Future<bool> clear() async {
|
||||
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
|
||||
return sharedPreferences.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
SliverGridDelegate gridDelegate(BuildContext context) {
|
||||
return SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: MediaQuery.of(context).size.width > 500.0 ? 4 : 2,
|
||||
mainAxisSpacing: 10,
|
||||
crossAxisSpacing: 10,
|
||||
childAspectRatio: 100 / 150,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:synchronized/synchronized.dart';
|
||||
|
||||
/// SharedPreferences Util.
|
||||
class SpUtil {
|
||||
static SpUtil? _singleton;
|
||||
static SharedPreferences? _prefs;
|
||||
static Lock _lock = Lock();
|
||||
|
||||
static Future<SpUtil?> getInstance() async {
|
||||
if (_singleton == null) {
|
||||
await _lock.synchronized(() async {
|
||||
if (_singleton == null) {
|
||||
// 保持本地实例直到完全初始化。
|
||||
var singleton = SpUtil._();
|
||||
await singleton._init();
|
||||
_singleton = singleton;
|
||||
}
|
||||
});
|
||||
}
|
||||
return _singleton;
|
||||
}
|
||||
|
||||
SpUtil._();
|
||||
|
||||
Future _init() async {
|
||||
_prefs = await SharedPreferences.getInstance();
|
||||
}
|
||||
|
||||
/// put object.
|
||||
static Future<bool>? putObject(String key, Object value) {
|
||||
if (_prefs == null) return null;
|
||||
return _prefs!.setString(key, value == null ? "" : json.encode(value));
|
||||
}
|
||||
|
||||
/// get object.
|
||||
static Map? getObject(String key) {
|
||||
if (_prefs == null) return null;
|
||||
String? _data = _prefs!.getString(key);
|
||||
return (_data == null || _data.isEmpty) ? null : json.decode(_data);
|
||||
}
|
||||
|
||||
/// get object.
|
||||
static Object? getObject2(String key) {
|
||||
if (_prefs == null) return null;
|
||||
String? _data = _prefs!.getString(key);
|
||||
return (_data == null || _data.isEmpty) ? null : json.decode(_data);
|
||||
}
|
||||
|
||||
/// put object list.
|
||||
static Future<bool>? putObjectList(String key, List<Object>? list) {
|
||||
if (_prefs == null) return null;
|
||||
List<String>? _dataList = list?.map((value) {
|
||||
return json.encode(value);
|
||||
}).toList();
|
||||
return _prefs!.setStringList(key, _dataList ?? []);
|
||||
}
|
||||
|
||||
/// get object list.
|
||||
static List<Map?>? getObjectList(String key) {
|
||||
if (_prefs == null) return null;
|
||||
List<String>? dataLis = _prefs!.getStringList(key);
|
||||
return dataLis?.map((value) {
|
||||
Map? _dataMap = json.decode(value);
|
||||
return _dataMap;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
/// get string.
|
||||
static String? getString(String key, {String? defValue = ''}) {
|
||||
if (_prefs == null) return defValue;
|
||||
return _prefs!.getString(key) ?? defValue;
|
||||
}
|
||||
|
||||
/// put string.
|
||||
static Future<bool>? putString(String? key, String value) {
|
||||
if (_prefs == null) return null;
|
||||
return _prefs!.setString(key!, value);
|
||||
}
|
||||
|
||||
/// get bool.
|
||||
static bool getBool(String key, {bool defValue = false}) {
|
||||
if (_prefs == null) return defValue;
|
||||
return _prefs!.getBool(key) ?? defValue;
|
||||
}
|
||||
|
||||
/// put bool.
|
||||
static Future<bool>? putBool(String key, bool value) {
|
||||
if (_prefs == null) return null;
|
||||
return _prefs!.setBool(key, value);
|
||||
}
|
||||
|
||||
/// get int.
|
||||
static int getInt(String key, {int defValue = 0}) {
|
||||
if (_prefs == null) return defValue;
|
||||
return _prefs!.getInt(key) ?? defValue;
|
||||
}
|
||||
|
||||
/// put int.
|
||||
static Future<bool>? putInt(String key, int value) {
|
||||
if (_prefs == null) return null;
|
||||
return _prefs!.setInt(key, value);
|
||||
}
|
||||
|
||||
/// get double.
|
||||
static double getDouble(String key, double defValue) {
|
||||
if (_prefs == null) return defValue;
|
||||
return _prefs!.getDouble(key) ?? defValue;
|
||||
}
|
||||
|
||||
/// put double.
|
||||
static Future<bool>? putDouble(String key, double value) {
|
||||
if (_prefs == null) return null;
|
||||
return _prefs!.setDouble(key, value);
|
||||
}
|
||||
|
||||
/// get string list.
|
||||
static List<String> getStringList(String key,
|
||||
{List<String> defValue = const []}) {
|
||||
if (_prefs == null) return defValue;
|
||||
return _prefs!.getStringList(key) ?? defValue;
|
||||
}
|
||||
|
||||
/// put string list.
|
||||
static Future<bool>? putStringList(String key, List<String> value) {
|
||||
if (_prefs == null) return null;
|
||||
return _prefs!.setStringList(key, value);
|
||||
}
|
||||
|
||||
/// get dynamic.
|
||||
static dynamic getDynamic(String key, {Object? defValue}) {
|
||||
if (_prefs == null) return defValue;
|
||||
return _prefs!.get(key) ?? defValue;
|
||||
}
|
||||
|
||||
/// have key.
|
||||
static bool? haveKey(String key) {
|
||||
if (_prefs == null) return null;
|
||||
return _prefs!.getKeys().contains(key);
|
||||
}
|
||||
|
||||
/// get keys.
|
||||
static Set<String>? getKeys() {
|
||||
if (_prefs == null) return null;
|
||||
return _prefs!.getKeys();
|
||||
}
|
||||
|
||||
/// remove.
|
||||
static Future<bool>? remove(String key) {
|
||||
if (_prefs == null) return null;
|
||||
return _prefs!.remove(key);
|
||||
}
|
||||
|
||||
/// clear.
|
||||
static Future<bool>? clear() {
|
||||
if (_prefs == null) return null;
|
||||
return _prefs!.clear();
|
||||
}
|
||||
|
||||
///Sp is initialized.
|
||||
static bool isInitialized() {
|
||||
return _prefs != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
enum Status { success, pending, empty, error, noConnection }
|
||||
@@ -0,0 +1,23 @@
|
||||
const welcomeText = 'Welcome';
|
||||
const welcomeContent = '';
|
||||
const getStarted = 'Get Started';
|
||||
const topMovies = 'Top Movies';
|
||||
const entering = 'entering';
|
||||
const choseFavoriteGenre =
|
||||
'Choose your interests and get the best movie recommendations. Don\'t worry, you can always change it later.';
|
||||
|
||||
const skip = 'Skip';
|
||||
const continui = 'Continue';
|
||||
const seeAll = 'See all';
|
||||
const populars = 'Popular';
|
||||
const topRated = 'Top rated movies';
|
||||
const home = 'Home';
|
||||
const explore = 'Explore';
|
||||
const myList = 'My List';
|
||||
const profile = 'Profile';
|
||||
const movies = 'Movies';
|
||||
const tryAgain = 'Try again';
|
||||
const errorMessage = 'Something went wrong!';
|
||||
const emptyMessage = 'Your List is Empty';
|
||||
const play = 'Play';
|
||||
const releaseDate = 'Release date: ';
|
||||
@@ -0,0 +1,27 @@
|
||||
class TransferUtil {
|
||||
double _transfer = 0;
|
||||
int _level = 0;
|
||||
List suffix = [
|
||||
'B',
|
||||
'KB',
|
||||
'MB',
|
||||
'GB',
|
||||
'TB',
|
||||
'PB'
|
||||
];
|
||||
|
||||
String toHumanReadable(int transfer) {
|
||||
_transfer = transfer.toDouble();
|
||||
handleTransfer();
|
||||
|
||||
return '${_transfer.toStringAsFixed(2)} ${suffix[_level]}';
|
||||
}
|
||||
|
||||
handleTransfer() {
|
||||
if (_transfer > 1024) {
|
||||
_transfer = _transfer / 1024;
|
||||
++_level;
|
||||
handleTransfer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
enum RequestStatus { loading, loaded, error }
|
||||
|
||||
enum GetAllRequestStatus { loading, loaded, error, fetchMoreError }
|
||||
@@ -0,0 +1,12 @@
|
||||
///无参数请求回调
|
||||
typedef ParamVoidCallback = dynamic Function();
|
||||
|
||||
///回调一个参数
|
||||
typedef ParamSingleCallback<D> = dynamic Function(D data);
|
||||
|
||||
///回到俩个参数
|
||||
typedef ParamTwiceCallback<O, T> = dynamic Function(O dataOne, T dataTwo);
|
||||
|
||||
///回调三个参数
|
||||
typedef ParamThreeCallback<O, T, K> = dynamic Function(
|
||||
O dataOne, T dataTwo, K threeData);
|
||||
@@ -0,0 +1,248 @@
|
||||
import 'package:flutter/material.dart';
|
||||
// import 'package:sail/go_router.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:sail/resources/app_colors.dart';
|
||||
import 'package:sail/resources/app_routes.dart';
|
||||
import 'package:sail/resources/app_values.dart';
|
||||
|
||||
String getDate(String? date) {
|
||||
if (date == null || date.isEmpty) {
|
||||
return '';
|
||||
}
|
||||
|
||||
final vals = date.split('-');
|
||||
String year = vals[0];
|
||||
int monthNb = int.parse(vals[1]);
|
||||
String day = vals[2];
|
||||
|
||||
String month = '';
|
||||
|
||||
switch (monthNb) {
|
||||
case 1:
|
||||
month = 'Jan';
|
||||
break;
|
||||
case 2:
|
||||
month = 'Feb';
|
||||
break;
|
||||
case 3:
|
||||
month = 'Mar';
|
||||
break;
|
||||
case 4:
|
||||
month = 'Apr';
|
||||
break;
|
||||
case 5:
|
||||
month = 'May';
|
||||
break;
|
||||
case 6:
|
||||
month = 'Jun';
|
||||
break;
|
||||
case 7:
|
||||
month = 'Jul';
|
||||
break;
|
||||
case 8:
|
||||
month = 'Aug';
|
||||
break;
|
||||
case 9:
|
||||
month = 'Sep';
|
||||
break;
|
||||
case 10:
|
||||
month = 'Oct';
|
||||
break;
|
||||
case 11:
|
||||
month = 'Nov';
|
||||
break;
|
||||
case 12:
|
||||
month = 'Dec';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return '$month $day, $year';
|
||||
}
|
||||
|
||||
String getPosterUrl(String? path) {
|
||||
if (path != null) {
|
||||
return ApiConstants.basePosterUrl + path;
|
||||
} else {
|
||||
return ApiConstants.moviePlaceHolder;
|
||||
}
|
||||
}
|
||||
|
||||
String getBackdropUrl(String? path) {
|
||||
if (path != null) {
|
||||
return ApiConstants.baseBackdropUrl + path;
|
||||
} else {
|
||||
return ApiConstants.moviePlaceHolder;
|
||||
}
|
||||
}
|
||||
|
||||
String getStillUrl(String? path) {
|
||||
if (path != null) {
|
||||
return ApiConstants.baseStillUrl + path;
|
||||
} else {
|
||||
return ApiConstants.stillPlaceHolder;
|
||||
}
|
||||
}
|
||||
|
||||
String getLength(int? runtime) {
|
||||
if (runtime == null || runtime == 0) {
|
||||
return '';
|
||||
}
|
||||
if (runtime < 60) {
|
||||
return '${runtime}m';
|
||||
}
|
||||
if (runtime % 60 == 0) {
|
||||
return '${runtime ~/ 60}h';
|
||||
}
|
||||
return '${runtime ~/ 60}h ${runtime % 60}m';
|
||||
}
|
||||
|
||||
String getVotesCount(int voteCount) {
|
||||
if (voteCount < 1000) {
|
||||
return '($voteCount)';
|
||||
}
|
||||
return '(${voteCount ~/ 1000}k)';
|
||||
}
|
||||
|
||||
String getProfileImageUrl(Map<String, dynamic> json) {
|
||||
if (json['profile_path'] != null) {
|
||||
return ApiConstants.baseProfileUrl + json['profile_path'];
|
||||
} else {
|
||||
return ApiConstants.castPlaceHolder;
|
||||
}
|
||||
}
|
||||
|
||||
class ApiConstants {
|
||||
static String castPlaceHolder = "";
|
||||
static String baseProfileUrl = "";
|
||||
|
||||
static var baseAvatarUrl = "";
|
||||
|
||||
static String avatarPlaceHolder = "";
|
||||
|
||||
static var baseVideoUrl = "";
|
||||
|
||||
static var basePosterUrl = "";
|
||||
|
||||
static String moviePlaceHolder = "";
|
||||
|
||||
static var baseStillUrl;
|
||||
|
||||
static var baseBackdropUrl;
|
||||
|
||||
static String stillPlaceHolder = "";
|
||||
}
|
||||
|
||||
String getElapsedTime(String date) {
|
||||
DateTime reviewDate = DateTime.parse(date);
|
||||
DateTime today = DateTime.now();
|
||||
|
||||
Duration diff = today.difference(reviewDate);
|
||||
if (diff.inDays >= 365) {
|
||||
int years = diff.inDays ~/ 365;
|
||||
return '${years}y';
|
||||
} else if (diff.inDays >= 30) {
|
||||
int months = diff.inDays ~/ 30;
|
||||
return '${months}mo';
|
||||
} else if (diff.inDays >= 7) {
|
||||
int weeks = diff.inDays ~/ 7;
|
||||
return '${weeks}w';
|
||||
} else if (diff.inDays >= 1) {
|
||||
return '${diff.inDays}d';
|
||||
} else if (diff.inHours >= 1) {
|
||||
int hours = diff.inHours ~/ 24;
|
||||
return '${hours}h';
|
||||
} else if (diff.inMinutes >= 1) {
|
||||
int minutes = diff.inDays ~/ 60;
|
||||
return '${minutes}min';
|
||||
} else {
|
||||
return 'Now';
|
||||
}
|
||||
}
|
||||
|
||||
String getGenres(List<dynamic> genres) {
|
||||
if (genres.isNotEmpty) {
|
||||
return genres.first['name'];
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
String getDateFromTimeSpan(int timestamp) {
|
||||
DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(timestamp);
|
||||
String formattedTime = DateFormat('yyyy-MM-dd HH:mm:ss').format(dateTime);
|
||||
return formattedTime;
|
||||
}
|
||||
|
||||
String getAvatarUrl(String? path) {
|
||||
if (path != null) {
|
||||
if (path.startsWith('/https://www.gravatar.com/avatar')) {
|
||||
return path.substring(1);
|
||||
} else {
|
||||
return ApiConstants.baseAvatarUrl + path;
|
||||
}
|
||||
} else {
|
||||
return ApiConstants.avatarPlaceHolder;
|
||||
}
|
||||
}
|
||||
|
||||
String getTrailerUrl(Map<String, dynamic> json) {
|
||||
List videos = json['videos']['results'];
|
||||
if (videos.isNotEmpty) {
|
||||
List trailers = videos.where((e) => e['type'] == 'Trailer').toList();
|
||||
if (trailers.isNotEmpty) {
|
||||
return ApiConstants.baseVideoUrl + trailers.last['key'];
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
void navigateToDetailsView(BuildContext context, Media media) {
|
||||
if (media.isMovie) {
|
||||
// context.pushNamed(
|
||||
// AppRoutes.movieDetailsRoute,
|
||||
// params: {'movieId': media.tmdbID.toString()},
|
||||
// );
|
||||
} else {
|
||||
// context.pushNamed(
|
||||
// AppRoutes.tvShowDetailsRoute,
|
||||
// params: {'tvShowId': media.tmdbID.toString()},
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
class Media {
|
||||
bool get isMovie => false;
|
||||
|
||||
get tmdbID => null;
|
||||
}
|
||||
|
||||
void showCustomBottomSheet(BuildContext context, Widget child) {
|
||||
final size = MediaQuery.of(context).size.height;
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: AppColors.secondaryBackground,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(AppSize.s20),
|
||||
),
|
||||
),
|
||||
builder: (context) {
|
||||
return SizedBox(
|
||||
height: size * 0.5,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void showSnackBar(BuildContext context, String content) {
|
||||
final snackBar = SnackBar(content: Text(content));
|
||||
ScaffoldMessenger.of(context)
|
||||
..hideCurrentSnackBar()
|
||||
..showSnackBar(snackBar);
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'dart:math';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
// ui适配
|
||||
class UiUtil {
|
||||
static const Size _defaultSize = Size(360, 690);
|
||||
|
||||
static UiUtil? _instance;
|
||||
|
||||
static UiUtil get instance => UiUtil();
|
||||
|
||||
/// UI设计中手机尺寸
|
||||
Size? _uiSize;
|
||||
|
||||
/// 控制字体是否要根据系统的“字体大小”辅助选项来进行缩放。默认值为false。
|
||||
late bool _allowFontScaling;
|
||||
|
||||
///屏幕方向
|
||||
Orientation? _orientation;
|
||||
|
||||
double? _pixelRatio;
|
||||
double? _textScaleFactor;
|
||||
late double _statusBarHeight;
|
||||
late double _bottomBarHeight;
|
||||
|
||||
double? _screenWidth;
|
||||
double? _screenHeight;
|
||||
|
||||
factory UiUtil() {
|
||||
if (_instance == null) {
|
||||
_instance = UiUtil._internal();
|
||||
}
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
UiUtil._internal() {
|
||||
var window = WidgetsBinding.instance.window;
|
||||
_pixelRatio = window.devicePixelRatio;
|
||||
_statusBarHeight = window.padding.top;
|
||||
_bottomBarHeight = window.padding.bottom;
|
||||
_textScaleFactor = window.textScaleFactor;
|
||||
_allowFontScaling = false;
|
||||
_uiSize = _defaultSize;
|
||||
}
|
||||
|
||||
static void init({
|
||||
BuildContext? context,
|
||||
Size designSize = _defaultSize,
|
||||
Orientation orientation = Orientation.portrait,
|
||||
bool allowFontScaling = false,
|
||||
}) {
|
||||
instance._init(
|
||||
context: context,
|
||||
designSize: designSize,
|
||||
orientation: orientation,
|
||||
allowFontScaling: allowFontScaling,
|
||||
);
|
||||
}
|
||||
|
||||
void _init({
|
||||
BuildContext? context,
|
||||
Size? designSize,
|
||||
Orientation orientation = Orientation.portrait,
|
||||
bool allowFontScaling = false,
|
||||
}) {
|
||||
this._uiSize = designSize;
|
||||
this._allowFontScaling = allowFontScaling;
|
||||
this._orientation = orientation;
|
||||
|
||||
if (orientation == Orientation.portrait) {
|
||||
this._screenWidth = MediaQuery.of(context!).size.width;
|
||||
this._screenHeight = MediaQuery.of(context).size.height;
|
||||
} else {
|
||||
this._screenWidth = MediaQuery.of(context!).size.height;
|
||||
this._screenHeight = MediaQuery.of(context).size.width;
|
||||
}
|
||||
}
|
||||
|
||||
///获取屏幕方向
|
||||
Orientation? get orientation => _orientation;
|
||||
|
||||
/// 每个逻辑像素的字体像素数,字体的缩放比例
|
||||
double? get textScaleFactor => _textScaleFactor;
|
||||
|
||||
/// 设备的像素密度
|
||||
double? get pixelRatio => _pixelRatio;
|
||||
|
||||
/// 当前设备宽度 dp
|
||||
double get screenWidth => _screenWidth ?? _defaultSize.width;
|
||||
|
||||
///当前设备高度 dp
|
||||
double get screenHeight => _screenHeight ?? _defaultSize.height;
|
||||
|
||||
/// 状态栏高度 dp 刘海屏会更高
|
||||
double get statusBarHeight => _statusBarHeight / _pixelRatio!;
|
||||
|
||||
/// 底部安全区距离 dp
|
||||
double get bottomBarHeight => _bottomBarHeight / _pixelRatio!;
|
||||
|
||||
/// 实际尺寸与UI设计的比例
|
||||
double get scaleWidth => (_screenWidth ?? _uiSize!.width) / _uiSize!.width;
|
||||
|
||||
double get scaleHeight => (_screenWidth ?? _uiSize!.height) / _uiSize!.height;
|
||||
|
||||
double get scaleText => min(scaleWidth, scaleHeight);
|
||||
|
||||
/// 根据UI设计的设备宽度适配
|
||||
double setWidth(num width) => width * scaleWidth;
|
||||
|
||||
/// 根据UI设计的设备高度适配
|
||||
/// 高度适配主要针对想根据UI设计的一屏展示一样的效果
|
||||
double setHeight(num height) => height * scaleHeight;
|
||||
|
||||
///根据宽度或高度中的较小值进行适配
|
||||
double radius(num r) => r * scaleText;
|
||||
|
||||
///字体大小适配方法
|
||||
double setSp(num fontSize, {bool? allowFontScalingSelf}) =>
|
||||
allowFontScalingSelf == null
|
||||
? (_allowFontScaling
|
||||
? (fontSize * scaleText) * _textScaleFactor!
|
||||
: (fontSize * scaleText))
|
||||
: (allowFontScalingSelf
|
||||
? (fontSize * scaleText) * _textScaleFactor!
|
||||
: (fontSize * scaleText));
|
||||
}
|
||||
|
||||
///适配文字
|
||||
@deprecated
|
||||
num setSp(num size) => UiUtil().setSp(size);
|
||||
|
||||
///自动适配,后面方便扩展
|
||||
@deprecated
|
||||
num auto(num size) => UiUtil().setWidth(size);
|
||||
|
||||
extension NumExtend on num {
|
||||
///自动适配移动界面
|
||||
double get dp {
|
||||
//如果没初始化,需要初始化,防止web端直接导航页面报错
|
||||
AutoUi.init();
|
||||
return UiUtil().setWidth(this);
|
||||
}
|
||||
|
||||
///配置文字,文字适配请用sp单位
|
||||
double get sp {
|
||||
//如果没初始化,需要初始化,防止web端直接导航页面报错
|
||||
AutoUi.init();
|
||||
return UiUtil().setWidth(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AutoUi {
|
||||
static AutoUi? _instance;
|
||||
|
||||
static void init() {
|
||||
if (_instance == null) {
|
||||
_instance = AutoUi();
|
||||
initAutoUi(Get.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//界面适配
|
||||
void initAutoUi(BuildContext? context) {
|
||||
if (context != null) return;
|
||||
|
||||
UiUtil.init(
|
||||
// 通过context获取设备像素大小
|
||||
context: context,
|
||||
// 设计尺寸
|
||||
// designSize: Size(1920 / 2.72, 1080 / 2.72),
|
||||
designSize: Size(1920, 1080),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user