Sfoglia il codice sorgente

[fit]1.修改新人页面生日间距
2.修改定制人设,爱好和特质自动滚动布局出现空缺的问题
3.调整新人结果页人设列表的宽度

云天逵 7 mesi fa
parent
commit
bf5c8d8f94

+ 39 - 53
lib/module/character_custom/character_custom_page.dart

@@ -312,59 +312,45 @@ class CharacterCustomPage extends BasePage<CharacterCustomController> {
           Container(
             margin: EdgeInsets.only(top: 32.h, left: 21.w, right: 21.w),
             height: 180.h,
-            child: AutoScrollListView(
-              itemCount: columnCount, // 列数
-              scrollDirection: Axis.horizontal,
-              itemBuilder: (context, columnIndex) {
-                //  3 个 item
-                int startIndex = columnIndex * rowCount;
-                List<dynamic> columnItems =
-                    items.skip(startIndex).take(rowCount).toList();
-                return Column(
-                  mainAxisSize: MainAxisSize.min,
-                  children:
-                      columnItems.map((item) {
-                        final emoji = item.emoji ?? "";
-                        final name = item.name ?? "";
-                        return Padding(
-                          padding: EdgeInsets.symmetric(
-                            vertical: 4.h,
-                            horizontal: 4.w,
-                          ),
-                          child: Obx(() {
-                            bool isSelected = selectedLabels.any(
-                              (selected) => selected.name == item.name,
-                            );
-                            return ChoiceChip(
-                              label: Text(
-                                isShowEmoji ? "$emoji$name" : name,
-                                style: TextStyle(
-                                  color:
-                                      isSelected
-                                          ? Colors.white
-                                          : const Color(0xFF755BAB),
-                                  fontSize: 14.sp,
-                                  fontWeight: FontWeight.w400,
-                                ),
-                              ),
-                              showCheckmark: false,
-                              selected: isSelected,
-                              selectedColor: const Color(0xFFB782FF),
-                              backgroundColor: Colors.white,
-                              shape: RoundedRectangleBorder(
-                                side: BorderSide(
-                                  width: 1.w,
-                                  color: const Color(0x4C755BAB),
-                                ),
-                                borderRadius: BorderRadius.circular(31.r),
-                              ),
-                              onSelected: (selected) {
-                                onSelected(item);
-                              },
-                            );
-                          }),
-                        );
-                      }).toList(),
+            child: StaggeredAutoScrollListView(
+              itemCount: items.length,
+              isAutoScrolling: true,
+              itemBuilder: (context, index) {
+                final item = items[index];
+                final emoji = item.emoji ?? "";
+                final name = item.name ?? "";
+
+                return Padding(
+                  padding: EdgeInsets.symmetric(vertical: 4.h, horizontal: 4.w),
+                  child: Obx(() {
+                    bool isSelected = selectedLabels.any(
+                          (selected) => selected.name == item.name,
+                    );
+                    return ChoiceChip(
+                      label: Text(
+                        isShowEmoji ? "$emoji$name" : name,
+                        style: TextStyle(
+                          color: isSelected ? Colors.white : const Color(0xFF755BAB),
+                          fontSize: 14.sp,
+                          fontWeight: FontWeight.w400,
+                        ),
+                      ),
+                      showCheckmark: false,
+                      selected: isSelected,
+                      selectedColor: const Color(0xFFB782FF),
+                      backgroundColor: Colors.white,
+                      shape: RoundedRectangleBorder(
+                        side: BorderSide(
+                          width: 1.w,
+                          color: const Color(0x4C755BAB),
+                        ),
+                        borderRadius: BorderRadius.circular(31.r),
+                      ),
+                      onSelected: (selected) {
+                        onSelected(item);
+                      },
+                    );
+                  }),
                 );
               },
             ),

+ 4 - 1
lib/module/new_user/result/new_user_result_page.dart

@@ -1,5 +1,6 @@
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
 import 'package:flutter/src/widgets/framework.dart';
 import 'package:flutter_screenutil/flutter_screenutil.dart';
 import 'package:keyboard/base/base_page.dart';
@@ -37,6 +38,8 @@ class NewUserResultPage extends BasePage<NewUserResultController> {
 
   @override
   Widget buildBody(BuildContext context) {
+
+
     return Stack(
       children: [
         Assets.images.bgNewUserResult.image(
@@ -258,7 +261,7 @@ class NewUserResultPage extends BasePage<NewUserResultController> {
             borderRadius: BorderRadius.circular(28.r),
           ),
         ),
-        margin: EdgeInsets.only(left: 21.w, right: 21.w),
+        margin: EdgeInsets.only(left: 16.w, right: 16.w),
 
         child: Column(
           children: [

+ 1 - 1
lib/module/new_user/step/birthday/step_birthday_view.dart

@@ -142,7 +142,7 @@ class StepBirthdayView extends BaseView<NewUserController> {
         initialDate: controller.initialDate,
         minimumDate: controller.minimumDate,
         maximumDate: DateTime.now(),
-
+          itemExtent: 40.w,
         onDateChanged: (date) {
           controller.updateConstellation(date);
         },

+ 122 - 0
lib/widget/auto_scroll_list_view.dart

@@ -2,6 +2,7 @@ import 'dart:async';
 
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/rendering.dart';
+import 'package:flutter_screenutil/flutter_screenutil.dart';
 
 class AutoScrollListView extends StatefulWidget {
   final NullableIndexedWidgetBuilder itemBuilder;
@@ -100,3 +101,124 @@ class _AutoScrollListViewState extends State<AutoScrollListView> {
     );
   }
 }
+
+
+// 多行横向自动滚动列表
+/// 一个支持自动滚动的多行横向列表,每一行自动向右滚动(模拟跑马灯效果)
+class StaggeredAutoScrollListView extends StatefulWidget {
+  /// 子项构建器(需根据 itemIndex 构建对应组件)
+  final NullableIndexedWidgetBuilder itemBuilder;
+
+  /// 实际数据项数量(不是扩展后的数量)
+  final int itemCount;
+
+  /// 每行的内边距
+  final EdgeInsetsGeometry? padding;
+
+  /// 是否启用自动滚动
+  final bool isAutoScrolling;
+
+  const StaggeredAutoScrollListView({
+    super.key,
+    required this.itemBuilder,
+    required this.itemCount,
+    this.padding,
+    this.isAutoScrolling = true,
+  });
+
+  @override
+  State<StaggeredAutoScrollListView> createState() => _StaggeredAutoScrollListViewState();
+}
+
+class _StaggeredAutoScrollListViewState extends State<StaggeredAutoScrollListView> {
+  final int rowCount = 3; // 列表的总行数
+  final List<ScrollController> _controllers = []; // 每一行的滚动控制器
+  final List<Timer?> _timers = []; // 每一行对应的自动滚动定时器
+  final int loopFactor = 3; // 每行内容扩展的倍数,防止滚动过短
+
+  @override
+  void initState() {
+    super.initState();
+
+    // 初始化每一行的滚动控制器
+    for (int i = 0; i < rowCount; i++) {
+      final controller = ScrollController();
+      _controllers.add(controller);
+    }
+
+    // 如果启用自动滚动,则在渲染后启动滚动逻辑
+    if (widget.isAutoScrolling) {
+      WidgetsBinding.instance.addPostFrameCallback((_) {
+        _startAllScrolls();
+      });
+    }
+  }
+
+  @override
+  void dispose() {
+    // 销毁滚动控制器
+    for (final controller in _controllers) {
+      controller.dispose();
+    }
+
+    // 取消所有定时器
+    for (final timer in _timers) {
+      timer?.cancel();
+    }
+
+    super.dispose();
+  }
+
+  /// 启动所有行的自动滚动
+  void _startAllScrolls() {
+    for (int i = 0; i < rowCount; i++) {
+      final controller = _controllers[i];
+
+      // 每 50ms 滚动一次,实现平滑向右滚动效果
+      final timer = Timer.periodic(const Duration(milliseconds: 50), (_) {
+        if (!controller.hasClients) return;
+
+        final maxExtent = controller.position.maxScrollExtent;
+        final current = controller.position.pixels;
+        final next = current + 1;
+
+        if (next >= maxExtent) {
+          // 若滚动到底部,则跳转到中间,避免突然回到顶部产生视觉跳变
+          controller.jumpTo(maxExtent / 2);
+        } else {
+          controller.jumpTo(next);
+        }
+      });
+
+      _timers.add(timer);
+    }
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    // 每一行应显示的实际数据项数(取整向上)
+    final actualItemsPerRow = (widget.itemCount / rowCount).ceil();
+
+    // 每一行扩展后的 item 数量(用于循环滚动)
+    final extendedItemsPerRow = actualItemsPerRow * loopFactor;
+
+    return Column(
+      children: List.generate(rowCount, (rowIndex) {
+        return SizedBox(
+          height: 50.w, // 每行高度,单位为屏幕适配宽度
+          child: ListView.builder(
+            controller: _controllers[rowIndex], // 设置对应行的滚动控制器
+            scrollDirection: Axis.horizontal,
+            padding: widget.padding,
+            itemCount: extendedItemsPerRow, // 扩展后的数量,制造无限滚动感
+            itemBuilder: (context, index) {
+              // 计算实际要展示的数据项索引
+              final itemIndex = (rowIndex * actualItemsPerRow + index) % widget.itemCount;
+              return widget.itemBuilder(context, itemIndex);
+            },
+          ),
+        );
+      }),
+    );
+  }
+}

+ 10 - 2
lib/widget/birthday_date_picker.dart

@@ -8,6 +8,10 @@ class BirthdayDatePicker extends StatefulWidget {
   final DateTime maximumDate;
   final void Function(DateTime) onDateChanged;
   final Color backgroundColor;
+  // 每一项的间距
+  final double itemExtent;
+
+  final double height;
 
   const BirthdayDatePicker({
     super.key,
@@ -16,6 +20,10 @@ class BirthdayDatePicker extends StatefulWidget {
     required this.maximumDate,
     required this.onDateChanged,
     this.backgroundColor = const Color.fromRGBO(255, 255, 255, 0.6),
+    this.itemExtent = 36,
+    this.height = 150,
+
+
   });
 
   @override
@@ -58,7 +66,7 @@ class _BirthdayDatePickerState extends State<BirthdayDatePicker> {
   @override
   Widget build(BuildContext context) {
     return Container(
-      height: 150.h,
+      height: widget.height,
       child: Stack(
         children: [
           Positioned(
@@ -145,7 +153,7 @@ class _BirthdayDatePickerState extends State<BirthdayDatePicker> {
     return Expanded(
       child: ListWheelScrollView.useDelegate(
         controller: controller,
-        itemExtent: 36.h,
+        itemExtent: widget.itemExtent,
         onSelectedItemChanged: onSelectedItemChanged,
         physics: FixedExtentScrollPhysics(),
         overAndUnderCenterOpacity: 0.35,