mirror of
https://ghfast.top/https://github.com/StarCitizenToolBox/app.git
synced 2025-08-16 07:08:04 +08:00
feat: 42kit Nav
feat: Animation Optimization
This commit is contained in:
41
lib/ui/nav/nav_state.dart
Normal file
41
lib/ui/nav/nav_state.dart
Normal file
@@ -0,0 +1,41 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:starcitizen_doctor/api/udb.dart';
|
||||
import 'package:starcitizen_doctor/data/nav_api_data.dart';
|
||||
|
||||
part 'nav_state.freezed.dart';
|
||||
|
||||
part 'nav_state.g.dart';
|
||||
|
||||
@freezed
|
||||
class NavState with _$NavState {
|
||||
const factory NavState({
|
||||
List<NavApiDocsItemData>? items,
|
||||
@Default("") String errorInfo,
|
||||
}) = _NavState;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class Nav extends _$Nav {
|
||||
bool _mounted = true;
|
||||
|
||||
@override
|
||||
NavState build() {
|
||||
state = NavState();
|
||||
loadData(1);
|
||||
ref.onDispose(() {
|
||||
_mounted = false;
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
void loadData(int pageNo) async {
|
||||
if (!_mounted) return;
|
||||
try {
|
||||
final r = await UDBNavApi.getNavItems(pageNo: pageNo);
|
||||
state = state.copyWith(items: r.docs, errorInfo: "");
|
||||
} catch (e) {
|
||||
state = state.copyWith(errorInfo: e.toString());
|
||||
}
|
||||
}
|
||||
}
|
173
lib/ui/nav/nav_state.freezed.dart
Normal file
173
lib/ui/nav/nav_state.freezed.dart
Normal file
@@ -0,0 +1,173 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'nav_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
/// @nodoc
|
||||
mixin _$NavState {
|
||||
List<NavApiDocsItemData>? get items => throw _privateConstructorUsedError;
|
||||
String get errorInfo => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of NavState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$NavStateCopyWith<NavState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $NavStateCopyWith<$Res> {
|
||||
factory $NavStateCopyWith(NavState value, $Res Function(NavState) then) =
|
||||
_$NavStateCopyWithImpl<$Res, NavState>;
|
||||
@useResult
|
||||
$Res call({List<NavApiDocsItemData>? items, String errorInfo});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$NavStateCopyWithImpl<$Res, $Val extends NavState>
|
||||
implements $NavStateCopyWith<$Res> {
|
||||
_$NavStateCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of NavState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? items = freezed,
|
||||
Object? errorInfo = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
items: freezed == items
|
||||
? _value.items
|
||||
: items // ignore: cast_nullable_to_non_nullable
|
||||
as List<NavApiDocsItemData>?,
|
||||
errorInfo: null == errorInfo
|
||||
? _value.errorInfo
|
||||
: errorInfo // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$NavStateImplCopyWith<$Res>
|
||||
implements $NavStateCopyWith<$Res> {
|
||||
factory _$$NavStateImplCopyWith(
|
||||
_$NavStateImpl value, $Res Function(_$NavStateImpl) then) =
|
||||
__$$NavStateImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({List<NavApiDocsItemData>? items, String errorInfo});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$NavStateImplCopyWithImpl<$Res>
|
||||
extends _$NavStateCopyWithImpl<$Res, _$NavStateImpl>
|
||||
implements _$$NavStateImplCopyWith<$Res> {
|
||||
__$$NavStateImplCopyWithImpl(
|
||||
_$NavStateImpl _value, $Res Function(_$NavStateImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of NavState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? items = freezed,
|
||||
Object? errorInfo = null,
|
||||
}) {
|
||||
return _then(_$NavStateImpl(
|
||||
items: freezed == items
|
||||
? _value._items
|
||||
: items // ignore: cast_nullable_to_non_nullable
|
||||
as List<NavApiDocsItemData>?,
|
||||
errorInfo: null == errorInfo
|
||||
? _value.errorInfo
|
||||
: errorInfo // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
|
||||
class _$NavStateImpl implements _NavState {
|
||||
const _$NavStateImpl(
|
||||
{final List<NavApiDocsItemData>? items, this.errorInfo = ""})
|
||||
: _items = items;
|
||||
|
||||
final List<NavApiDocsItemData>? _items;
|
||||
@override
|
||||
List<NavApiDocsItemData>? get items {
|
||||
final value = _items;
|
||||
if (value == null) return null;
|
||||
if (_items is EqualUnmodifiableListView) return _items;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(value);
|
||||
}
|
||||
|
||||
@override
|
||||
@JsonKey()
|
||||
final String errorInfo;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'NavState(items: $items, errorInfo: $errorInfo)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$NavStateImpl &&
|
||||
const DeepCollectionEquality().equals(other._items, _items) &&
|
||||
(identical(other.errorInfo, errorInfo) ||
|
||||
other.errorInfo == errorInfo));
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, const DeepCollectionEquality().hash(_items), errorInfo);
|
||||
|
||||
/// Create a copy of NavState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$NavStateImplCopyWith<_$NavStateImpl> get copyWith =>
|
||||
__$$NavStateImplCopyWithImpl<_$NavStateImpl>(this, _$identity);
|
||||
}
|
||||
|
||||
abstract class _NavState implements NavState {
|
||||
const factory _NavState(
|
||||
{final List<NavApiDocsItemData>? items,
|
||||
final String errorInfo}) = _$NavStateImpl;
|
||||
|
||||
@override
|
||||
List<NavApiDocsItemData>? get items;
|
||||
@override
|
||||
String get errorInfo;
|
||||
|
||||
/// Create a copy of NavState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$NavStateImplCopyWith<_$NavStateImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
24
lib/ui/nav/nav_state.g.dart
Normal file
24
lib/ui/nav/nav_state.g.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'nav_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$navHash() => r'2019b3f675fbaec4be794049d900bf2dcc8d5e37';
|
||||
|
||||
/// See also [Nav].
|
||||
@ProviderFor(Nav)
|
||||
final navProvider = AutoDisposeNotifierProvider<Nav, NavState>.internal(
|
||||
Nav.new,
|
||||
name: r'navProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$navHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$Nav = AutoDisposeNotifier<NavState>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
203
lib/ui/nav/nav_ui.dart
Normal file
203
lib/ui/nav/nav_ui.dart
Normal file
@@ -0,0 +1,203 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/gestures.dart' show TapGestureRecognizer;
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||
import 'package:flutter_tilt/flutter_tilt.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:starcitizen_doctor/ui/nav/nav_state.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
|
||||
class NavUI extends HookConsumerWidget {
|
||||
const NavUI({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: buildBody(context, ref),
|
||||
),
|
||||
SizedBox(height: 6),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text.rich(
|
||||
TextSpan(children: [
|
||||
TextSpan(text: "*对应链接指向的服务由第三方提供,我们不对其做任何担保,请用户自行判断使用风险 | "),
|
||||
TextSpan(text: "网站导航数据由"),
|
||||
TextSpan(
|
||||
text: " 42kit ",
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
launchUrlString("https://42kit.citizenwiki.cn/nav");
|
||||
},
|
||||
),
|
||||
TextSpan(text: "提供"),
|
||||
]),
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.white.withValues(alpha: .6),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildBody(BuildContext context, WidgetRef ref) {
|
||||
final data = ref.watch(navProvider);
|
||||
if (data.errorInfo.isNotEmpty) {
|
||||
return Center(
|
||||
child: Text(
|
||||
data.errorInfo,
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.items == null) {
|
||||
return const Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ProgressRing(),
|
||||
SizedBox(height: 12),
|
||||
Text("正在获取数据..."),
|
||||
],
|
||||
));
|
||||
}
|
||||
return MasonryGridView.count(
|
||||
crossAxisCount: 3,
|
||||
mainAxisSpacing: 12,
|
||||
crossAxisSpacing: 12,
|
||||
itemCount: data.items!.length,
|
||||
padding: EdgeInsets.only(left: 12, right: 12, bottom: 12),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
const itemHeight = 160.0;
|
||||
|
||||
final item = data.items![index];
|
||||
final itemName = item.name;
|
||||
final itemImage = item.image.url;
|
||||
return GridItemAnimator(
|
||||
index: index,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
launchUrlString(item.link);
|
||||
},
|
||||
child: Tilt(
|
||||
shadowConfig: const ShadowConfig(maxIntensity: .3),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: SizedBox(
|
||||
height: itemHeight,
|
||||
width: double.infinity,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: Stack(
|
||||
children: [
|
||||
Center(
|
||||
child: CacheNetImage(
|
||||
height: itemHeight,
|
||||
width: double.infinity,
|
||||
url: itemImage,
|
||||
fit: BoxFit.fitWidth,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withValues(alpha: .55),
|
||||
),
|
||||
),
|
||||
ClipRect(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 15.0, sigmaY: 15.0),
|
||||
blendMode: BlendMode.srcOver,
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: itemHeight,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(),
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: CacheNetImage(
|
||||
url: itemImage,
|
||||
height: 48,
|
||||
width: 48,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Flexible(
|
||||
child: Text(
|
||||
itemName,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
item.abstract_,
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(color: Colors.white.withValues(alpha: .75)),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
for (var value in item.tags)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue.withValues(alpha: .6),
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 3),
|
||||
margin: EdgeInsets.only(right: 6),
|
||||
child: Text(
|
||||
value.name,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user