feat: 优化前端 UI 与交互体验
This commit is contained in:
@@ -16,6 +16,7 @@ import '../../../messages/data/inbox_api.dart';
|
||||
import '../../data/voice_recorder.dart';
|
||||
import '../../../chat/ui/widgets/ui_schema_renderer.dart';
|
||||
import '../../../../shared/widgets/app_loading_indicator.dart';
|
||||
import '../../../../shared/widgets/app_pull_refresh_feedback.dart';
|
||||
import '../../../../shared/widgets/message_composer.dart';
|
||||
import '../../../../shared/widgets/toast/toast.dart';
|
||||
import '../../../../shared/widgets/toast/toast_type.dart';
|
||||
@@ -95,6 +96,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
bool _isTranscribing = false;
|
||||
bool _isCancelGestureActive = false;
|
||||
bool _isSendingMessage = false;
|
||||
bool _isPullRefreshing = false;
|
||||
int _unreadCount = 0;
|
||||
final List<XFile> _selectedImages = [];
|
||||
|
||||
@@ -210,7 +212,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
const Positioned.fill(child: _HomeEmptyStateAmbient())
|
||||
else
|
||||
Positioned.fill(
|
||||
child: RefreshIndicator(
|
||||
child: RefreshIndicator.noSpinner(
|
||||
onRefresh: () => _onRefresh(context),
|
||||
child: ListView.builder(
|
||||
controller: _scrollController,
|
||||
@@ -260,6 +262,10 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
alignment: Alignment.bottomLeft,
|
||||
child: _buildWaitingIndicator(currentStage: state.currentStage),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: AppPullRefreshFeedback(visible: _isPullRefreshing),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -347,7 +353,19 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
}
|
||||
|
||||
Future<void> _onRefresh(BuildContext context) async {
|
||||
await context.read<ChatBloc>().loadMoreHistory();
|
||||
if (_isPullRefreshing) {
|
||||
return;
|
||||
}
|
||||
if (mounted) {
|
||||
setState(() => _isPullRefreshing = true);
|
||||
}
|
||||
try {
|
||||
await context.read<ChatBloc>().loadMoreHistory();
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() => _isPullRefreshing = false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _onLoadMore(BuildContext context) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:lucide_icons/lucide_icons.dart';
|
||||
import '../../../../core/theme/design_tokens.dart';
|
||||
|
||||
const homeFloatingHeaderKey = ValueKey('home_floating_header');
|
||||
const homeFloatingHeaderTitleKey = ValueKey('home_floating_header_title');
|
||||
|
||||
class HomeFloatingHeader extends StatelessWidget {
|
||||
const HomeFloatingHeader({
|
||||
@@ -21,60 +22,62 @@ class HomeFloatingHeader extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
return Container(
|
||||
key: homeFloatingHeaderKey,
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
AppSpacing.lg,
|
||||
AppSpacing.sm,
|
||||
AppSpacing.lg,
|
||||
AppSpacing.md,
|
||||
AppSpacing.sm,
|
||||
),
|
||||
child: Container(
|
||||
key: homeFloatingHeaderKey,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.sm,
|
||||
vertical: AppSpacing.xs,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.homeToolbarSurface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.full),
|
||||
border: Border.all(color: AppColors.homeToolbarBorder),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: AppColors.white,
|
||||
blurRadius: AppRadius.md,
|
||||
offset: Offset(0, -(AppSpacing.xs / 2)),
|
||||
),
|
||||
BoxShadow(
|
||||
color: AppColors.slate200,
|
||||
blurRadius: AppRadius.lg,
|
||||
offset: Offset(0, AppSpacing.sm - (AppSpacing.xs / 2)),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_HeaderIconButton(
|
||||
icon: LucideIcons.settings,
|
||||
onPressed: onTapSettings,
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_HeaderIconButton(
|
||||
icon: LucideIcons.calendar,
|
||||
onPressed: onTapCalendar,
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColors.homeToolbarSurface,
|
||||
border: Border(bottom: BorderSide(color: AppColors.homeToolbarBorder)),
|
||||
),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_HeaderIconButton(
|
||||
icon: LucideIcons.settings,
|
||||
onPressed: onTapSettings,
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_HeaderIconButton(
|
||||
icon: LucideIcons.calendar,
|
||||
onPressed: onTapCalendar,
|
||||
),
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
_MessagesButton(
|
||||
unreadCount: unreadCount,
|
||||
onPressed: onTapMessages,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const IgnorePointer(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: AppSpacing.xl * 3),
|
||||
child: Text(
|
||||
'Linksy',
|
||||
key: homeFloatingHeaderTitleKey,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: AppSpacing.lg + (AppSpacing.xs / 2),
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate800,
|
||||
),
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
_MessagesButton(
|
||||
unreadCount: unreadCount,
|
||||
onPressed: onTapMessages,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user