mirror of
https://github.com/simon-ding/polaris.git
synced 2026-04-21 19:27:30 +08:00
feat: use cookie to store jwt token, better performance
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
|||||||
"polaris/db"
|
"polaris/db"
|
||||||
"polaris/log"
|
"polaris/log"
|
||||||
"polaris/pkg/utils"
|
"polaris/pkg/utils"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -23,16 +22,15 @@ func (s *Server) authModdleware(c *gin.Context) {
|
|||||||
c.Next()
|
c.Next()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
token, err := c.Cookie("token")
|
||||||
auth := c.GetHeader("Authorization")
|
if err != nil {
|
||||||
if auth == "" {
|
log.Errorf("token error: %v", err)
|
||||||
log.Infof("token is not present, abort")
|
|
||||||
c.AbortWithStatus(http.StatusForbidden)
|
c.AbortWithStatus(http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
auth = strings.TrimPrefix(auth, "Bearer ")
|
|
||||||
//log.Debugf("current token: %v", auth)
|
//log.Debugf("current token: %v", auth)
|
||||||
token, err := jwt.ParseWithClaims(auth, &jwt.RegisteredClaims{}, func(t *jwt.Token) (interface{}, error) {
|
tokenParsed, err := jwt.ParseWithClaims(token, &jwt.RegisteredClaims{}, func(t *jwt.Token) (interface{}, error) {
|
||||||
return []byte(s.jwtSerect), nil
|
return []byte(s.jwtSerect), nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -40,15 +38,15 @@ func (s *Server) authModdleware(c *gin.Context) {
|
|||||||
c.AbortWithStatus(http.StatusForbidden)
|
c.AbortWithStatus(http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !token.Valid {
|
if !tokenParsed.Valid {
|
||||||
log.Errorf("token is not valid: %v", auth)
|
log.Errorf("token is not valid: %v", token)
|
||||||
c.AbortWithStatus(http.StatusForbidden)
|
c.AbortWithStatus(http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
claim := token.Claims.(*jwt.RegisteredClaims)
|
claim := tokenParsed.Claims.(*jwt.RegisteredClaims)
|
||||||
|
|
||||||
if time.Until(claim.ExpiresAt.Time) <= 0 {
|
if time.Until(claim.ExpiresAt.Time) <= 0 {
|
||||||
log.Infof("token is no longer valid: %s", auth)
|
log.Infof("token is no longer valid: %s", token)
|
||||||
c.AbortWithStatus(http.StatusForbidden)
|
c.AbortWithStatus(http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -61,7 +59,6 @@ type LoginIn struct {
|
|||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func (s *Server) Login(c *gin.Context) (interface{}, error) {
|
func (s *Server) Login(c *gin.Context) (interface{}, error) {
|
||||||
var in LoginIn
|
var in LoginIn
|
||||||
|
|
||||||
@@ -93,11 +90,23 @@ func (s *Server) Login(c *gin.Context) (interface{}, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "sign")
|
return nil, errors.Wrap(err, "sign")
|
||||||
}
|
}
|
||||||
|
c.SetSameSite(http.SameSiteNoneMode)
|
||||||
|
c.SetCookie("token", sig, 0, "/", "", true, false)
|
||||||
return gin.H{
|
return gin.H{
|
||||||
"token": sig,
|
"token": sig,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) Logout(c *gin.Context) (interface{}, error) {
|
||||||
|
if !s.isAuthEnabled() {
|
||||||
|
return nil, errors.New( "auth is not enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetSameSite(http.SameSiteNoneMode)
|
||||||
|
c.SetCookie("token", "", -1, "/", "", true, false)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
type EnableAuthIn struct {
|
type EnableAuthIn struct {
|
||||||
Enable bool `json:"enable"`
|
Enable bool `json:"enable"`
|
||||||
User string `json:"user"`
|
User string `json:"user"`
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ func (s *Server) Serve() error {
|
|||||||
|
|
||||||
setting := api.Group("/setting")
|
setting := api.Group("/setting")
|
||||||
{
|
{
|
||||||
|
setting.GET("/logout", HttpHandler(s.Logout))
|
||||||
setting.POST("/general", HttpHandler(s.SetSetting))
|
setting.POST("/general", HttpHandler(s.SetSetting))
|
||||||
setting.GET("/general", HttpHandler(s.GetSetting))
|
setting.GET("/general", HttpHandler(s.GetSetting))
|
||||||
setting.POST("/auth", HttpHandler(s.EnableAuth))
|
setting.POST("/auth", HttpHandler(s.EnableAuth))
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart';
|
import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:ui/activity.dart';
|
import 'package:ui/activity.dart';
|
||||||
import 'package:ui/login_page.dart';
|
import 'package:ui/login_page.dart';
|
||||||
import 'package:ui/movie_watchlist.dart';
|
import 'package:ui/movie_watchlist.dart';
|
||||||
@@ -177,41 +176,30 @@ class _MainSkeletonState extends State<MainSkeleton> {
|
|||||||
(BuildContext context, SearchController controller) {
|
(BuildContext context, SearchController controller) {
|
||||||
return [Text("dadada")];
|
return [Text("dadada")];
|
||||||
}),
|
}),
|
||||||
FutureBuilder(
|
MenuAnchor(
|
||||||
future: APIs.isLoggedIn(),
|
menuChildren: [
|
||||||
builder: (context, snapshot) {
|
MenuItemButton(
|
||||||
if (snapshot.hasData && snapshot.data == true) {
|
leadingIcon: const Icon(Icons.exit_to_app),
|
||||||
return MenuAnchor(
|
child: const Text("登出"),
|
||||||
menuChildren: [
|
onPressed: () async {
|
||||||
MenuItemButton(
|
await APIs.logout();
|
||||||
leadingIcon: const Icon(Icons.exit_to_app),
|
},
|
||||||
child: const Text("登出"),
|
),
|
||||||
onPressed: () async {
|
],
|
||||||
final SharedPreferences prefs =
|
builder: (context, controller, child) {
|
||||||
await SharedPreferences.getInstance();
|
return TextButton(
|
||||||
await prefs.remove('token');
|
onPressed: () {
|
||||||
if (context.mounted) {
|
if (controller.isOpen) {
|
||||||
context.go(LoginScreen.route);
|
controller.close();
|
||||||
}
|
} else {
|
||||||
},
|
controller.open();
|
||||||
),
|
}
|
||||||
],
|
},
|
||||||
builder: (context, controller, child) {
|
child: const Icon(Icons.account_circle),
|
||||||
return TextButton(
|
);
|
||||||
onPressed: () {
|
},
|
||||||
if (controller.isOpen) {
|
)
|
||||||
controller.close();
|
|
||||||
} else {
|
|
||||||
controller.open();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: const Icon(Icons.account_circle),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return Container();
|
|
||||||
})
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
useDrawer: false,
|
useDrawer: false,
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ class _MovieDetailsPageState extends ConsumerState<MovieDetailsPage> {
|
|||||||
fit: BoxFit.fitWidth,
|
fit: BoxFit.fitWidth,
|
||||||
opacity: 0.5,
|
opacity: 0.5,
|
||||||
image: NetworkImage(
|
image: NetworkImage(
|
||||||
"${APIs.imagesUrl}/${details.id}/backdrop.jpg",
|
"${APIs.imagesUrl}/${details.id}/backdrop.jpg",
|
||||||
headers: APIs.authHeaders))),
|
))),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -58,7 +58,6 @@ class _MovieDetailsPageState extends ConsumerState<MovieDetailsPage> {
|
|||||||
child: Image.network(
|
child: Image.network(
|
||||||
"${APIs.imagesUrl}/${details.id}/poster.jpg",
|
"${APIs.imagesUrl}/${details.id}/poster.jpg",
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
headers: APIs.authHeaders,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import 'package:dio/dio.dart';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:quiver/strings.dart';
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:ui/providers/server_response.dart';
|
import 'package:ui/providers/server_response.dart';
|
||||||
|
|
||||||
class APIs {
|
class APIs {
|
||||||
@@ -26,6 +24,7 @@ class APIs {
|
|||||||
static final delDownloadClientUrl = "$_baseUrl/api/v1/downloader/del/";
|
static final delDownloadClientUrl = "$_baseUrl/api/v1/downloader/del/";
|
||||||
static final storageUrl = "$_baseUrl/api/v1/storage/";
|
static final storageUrl = "$_baseUrl/api/v1/storage/";
|
||||||
static final loginUrl = "$_baseUrl/api/login";
|
static final loginUrl = "$_baseUrl/api/login";
|
||||||
|
static final logoutUrl = "$_baseUrl/api/v1/setting/logout";
|
||||||
static final loginSettingUrl = "$_baseUrl/api/v1/setting/auth";
|
static final loginSettingUrl = "$_baseUrl/api/v1/setting/auth";
|
||||||
static final activityUrl = "$_baseUrl/api/v1/activity/";
|
static final activityUrl = "$_baseUrl/api/v1/activity/";
|
||||||
static final activityMediaUrl = "$_baseUrl/api/v1/activity/media/";
|
static final activityMediaUrl = "$_baseUrl/api/v1/activity/media/";
|
||||||
@@ -49,53 +48,20 @@ class APIs {
|
|||||||
return "http://127.0.0.1:8080";
|
return "http://127.0.0.1:8080";
|
||||||
}
|
}
|
||||||
|
|
||||||
static Dio? gDio;
|
static Dio getDio() {
|
||||||
static Map<String, String> authHeaders = {};
|
|
||||||
|
|
||||||
static Future<bool> isLoggedIn() async {
|
|
||||||
return isNotBlank(await getToken());
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<String> getToken() async {
|
|
||||||
var token = authHeaders["Authorization"];
|
|
||||||
if (isBlank(token)) {
|
|
||||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
var t = prefs.getString("token");
|
|
||||||
if (isNotBlank(t)) {
|
|
||||||
authHeaders["Authorization"] = t!;
|
|
||||||
token = t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return token ?? "";
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<Dio> getDio() async {
|
|
||||||
if (gDio != null) {
|
|
||||||
return gDio!;
|
|
||||||
}
|
|
||||||
var token = await getToken();
|
|
||||||
|
|
||||||
var dio = Dio();
|
var dio = Dio();
|
||||||
dio.interceptors.add(InterceptorsWrapper(
|
dio.interceptors.add(InterceptorsWrapper(
|
||||||
onRequest: (options, handler) {
|
|
||||||
options.headers['Authorization'] = token;
|
|
||||||
return handler.next(options);
|
|
||||||
},
|
|
||||||
onError: (error, handler) {
|
onError: (error, handler) {
|
||||||
if (error.response?.statusCode != null &&
|
if (error.response?.statusCode != null &&
|
||||||
error.response?.statusCode! == 403) {
|
error.response?.statusCode! == 403) {
|
||||||
final context = navigatorKey.currentContext;
|
final context = navigatorKey.currentContext;
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
context.go('/login');
|
context.go('/login');
|
||||||
gDio = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return handler.next(error);
|
return handler.next(error);
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
if (isNotBlank(token)) {
|
|
||||||
gDio = dio;
|
|
||||||
}
|
|
||||||
return dio;
|
return dio;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,9 +74,19 @@ class APIs {
|
|||||||
if (sp.code != 0) {
|
if (sp.code != 0) {
|
||||||
throw sp.message;
|
throw sp.message;
|
||||||
}
|
}
|
||||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
}
|
||||||
var t = sp.data["token"];
|
|
||||||
authHeaders["Authorization"] = "Bearer $t";
|
static Future<void> logout() async {
|
||||||
prefs.setString("token", "Bearer $t");
|
var resp = await getDio().get(APIs.logoutUrl);
|
||||||
|
|
||||||
|
var sp = ServerResponse.fromJson(resp.data);
|
||||||
|
|
||||||
|
if (sp.code != 0) {
|
||||||
|
throw sp.message;
|
||||||
|
}
|
||||||
|
final context = navigatorKey.currentContext;
|
||||||
|
if (context != null) {
|
||||||
|
context.go('/login');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ class _SearchPageState extends ConsumerState<SearchPage> {
|
|||||||
child: Image.network(
|
child: Image.network(
|
||||||
"${APIs.tmdbImgBaseUrl}${item.posterPath}",
|
"${APIs.tmdbImgBaseUrl}${item.posterPath}",
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
headers: APIs.authHeaders,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -49,9 +49,7 @@ class _SystemPageState extends ConsumerState<SystemPage> {
|
|||||||
DataCell(Text((item.size ?? 0).readableFileSize())),
|
DataCell(Text((item.size ?? 0).readableFileSize())),
|
||||||
DataCell(InkWell(
|
DataCell(InkWell(
|
||||||
child: const Icon(Icons.download),
|
child: const Icon(Icons.download),
|
||||||
onTap: () => launchUrl(uri,
|
onTap: () => launchUrl(uri),
|
||||||
webViewConfiguration: WebViewConfiguration(
|
|
||||||
headers: APIs.authHeaders)),
|
|
||||||
))
|
))
|
||||||
]);
|
]);
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -155,8 +155,7 @@ class _TvDetailsPageState extends ConsumerState<TvDetailsPage> {
|
|||||||
fit: BoxFit.fitWidth,
|
fit: BoxFit.fitWidth,
|
||||||
opacity: 0.5,
|
opacity: 0.5,
|
||||||
image: NetworkImage(
|
image: NetworkImage(
|
||||||
"${APIs.imagesUrl}/${details.id}/backdrop.jpg",
|
"${APIs.imagesUrl}/${details.id}/backdrop.jpg"))),
|
||||||
headers: APIs.authHeaders))),
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -167,8 +166,7 @@ class _TvDetailsPageState extends ConsumerState<TvDetailsPage> {
|
|||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
"${APIs.imagesUrl}/${details.id}/poster.jpg",
|
"${APIs.imagesUrl}/${details.id}/poster.jpg",
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain
|
||||||
headers: APIs.authHeaders,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -34,7 +34,10 @@ class WelcomePage extends ConsumerWidget {
|
|||||||
Container(
|
Container(
|
||||||
height: MediaQuery.of(context).size.height * 0.6,
|
height: MediaQuery.of(context).size.height * 0.6,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: const Text("啥都没有...", style: TextStyle(fontSize: 16),))
|
child: const Text(
|
||||||
|
"啥都没有...",
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
))
|
||||||
]
|
]
|
||||||
: List.generate(value.length, (i) {
|
: List.generate(value.length, (i) {
|
||||||
var item = value[i];
|
var item = value[i];
|
||||||
@@ -56,10 +59,8 @@ class WelcomePage extends ConsumerWidget {
|
|||||||
width: 140,
|
width: 140,
|
||||||
height: 210,
|
height: 210,
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
"${APIs.imagesUrl}/${item.id}/poster.jpg",
|
"${APIs.imagesUrl}/${item.id}/poster.jpg",
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.fill),
|
||||||
headers: APIs.authHeaders,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 140,
|
width: 140,
|
||||||
|
|||||||
112
ui/pubspec.lock
112
ui/pubspec.lock
@@ -89,22 +89,6 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.1"
|
||||||
ffi:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: ffi
|
|
||||||
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.2"
|
|
||||||
file:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: file
|
|
||||||
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "7.0.0"
|
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -325,30 +309,6 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.0"
|
version: "1.9.0"
|
||||||
path_provider_linux:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path_provider_linux
|
|
||||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.1"
|
|
||||||
path_provider_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path_provider_platform_interface
|
|
||||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.2"
|
|
||||||
path_provider_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: path_provider_windows
|
|
||||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "2.3.0"
|
|
||||||
percent_indicator:
|
percent_indicator:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -365,14 +325,6 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.3.0"
|
version: "8.3.0"
|
||||||
platform:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: platform
|
|
||||||
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.5"
|
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -405,62 +357,6 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.1"
|
version: "2.5.1"
|
||||||
shared_preferences:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: shared_preferences
|
|
||||||
sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.3"
|
|
||||||
shared_preferences_android:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_android
|
|
||||||
sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577"
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "2.2.3"
|
|
||||||
shared_preferences_foundation:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_foundation
|
|
||||||
sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7"
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "2.4.0"
|
|
||||||
shared_preferences_linux:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_linux
|
|
||||||
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "2.3.2"
|
|
||||||
shared_preferences_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_platform_interface
|
|
||||||
sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "2.3.2"
|
|
||||||
shared_preferences_web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_web
|
|
||||||
sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a"
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "2.3.0"
|
|
||||||
shared_preferences_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_windows
|
|
||||||
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "2.3.2"
|
|
||||||
sign_in_button:
|
sign_in_button:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -626,14 +522,6 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.1"
|
version: "0.5.1"
|
||||||
xdg_directories:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: xdg_directories
|
|
||||||
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
|
|
||||||
url: "https://pub.flutter-io.cn"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.4"
|
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.4.3 <4.0.0"
|
dart: ">=3.4.3 <4.0.0"
|
||||||
flutter: ">=3.22.0"
|
flutter: ">=3.22.0"
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ dependencies:
|
|||||||
flutter_riverpod: ^2.5.1
|
flutter_riverpod: ^2.5.1
|
||||||
quiver: ^3.2.1
|
quiver: ^3.2.1
|
||||||
flutter_login: ^5.0.0
|
flutter_login: ^5.0.0
|
||||||
shared_preferences: ^2.2.3
|
|
||||||
percent_indicator: ^4.2.3
|
percent_indicator: ^4.2.3
|
||||||
intl: ^0.19.0
|
intl: ^0.19.0
|
||||||
flutter_adaptive_scaffold: ^0.1.11+1
|
flutter_adaptive_scaffold: ^0.1.11+1
|
||||||
|
|||||||
Reference in New Issue
Block a user