Skip to content

flutter 常用代码片段

表格

你可以使用 FlutterDataTable 组件来创建表格,并将其包装在 SingleChildScrollView 中以支持横向滚动。以下是一个简单的示例代码,用于创建包含 10 个属性的表格:

dart
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter DataTable'),
        ),
        body: SingleChildScrollView(
          scrollDirection: Axis.horizontal,
          child: DataTable(
            columnSpacing: 20.0,
            columns: <DataColumn>[
              DataColumn(
                label: Text(
                  '属性1',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
              ),
              DataColumn(
                label: Text(
                  '属性2',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
              ),
              DataColumn(
                label: Text(
                  '属性3',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
              ),
              DataColumn(
                label: Text(
                  '属性4',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
              ),
              DataColumn(
                label: Text(
                  '属性5',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
              ),
              DataColumn(
                label: Text(
                  '属性6',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
              ),
              DataColumn(
                label: Text(
                  '属性7',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
              ),
              DataColumn(
                label: Text(
                  '属性8',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
              ),
              DataColumn(
                label: Text(
                  '属性9',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
              ),
              DataColumn(
                label: Text(
                  '属性10',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
              ),
            ],
            rows: List<DataRow>.generate(
              10,
              (int index) => DataRow(
                cells: <DataCell>[
                  DataCell(Text('值${index + 1}')),
                  DataCell(Text('值${index + 2}')),
                  DataCell(Text('值${index + 3}')),
                  DataCell(Text('值${index + 4}')),
                  DataCell(Text('值${index + 5}')),
                  DataCell(Text('值${index + 6}')),
                  DataCell(Text('值${index + 7}')),
                  DataCell(Text('值${index + 8}')),
                  DataCell(Text('值${index + 9}')),
                  DataCell(Text('值${index + 10}')),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

appBar 下面添加内容,并且悬浮

dart
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
            pinned: true, // 设置AppBar固定在顶部
            expandedHeight: 200.0,
            flexibleSpace: FlexibleSpaceBar(
              title: Text('Page Title'),
            ),
          ),
          SliverToBoxAdapter(
            child: Container(
              color: Colors.grey.shade300,
              padding: const EdgeInsets.all(16.0),
              child: Text(
                '这里是AppBar下面的一行文字',
                style: TextStyle(fontSize: 18),
              ),
            ),
          ),
          // 列表内容
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (context, index) {
                return Container(
                  padding: const EdgeInsets.all(16.0),
                  color: Colors.primaries[index % Colors.primaries.length],
                  child: Text(
                    'Item ${index + 1}',
                    style: TextStyle(fontSize: 20),
                  ),
                );
              },
              childCount: 100,
            ),
          ),
        ],
      ),
    );
  }
}

ListTitle 编写用户中心栏目列表

使用 ListTile 来创建用户中心栏目列表,每一项包括一个图标、文字和右边的箭头符号:

dart
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // 用户中心栏目数据
  final List<Map<String, dynamic>> userMenuItems = [
    {'icon': Icons.person, 'text': '个人资料'},
    {'icon': Icons.payment, 'text': '我的订单'},
    {'icon': Icons.favorite, 'text': '我的收藏'},
    {'icon': Icons.settings, 'text': '设置'},
  ];

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('User Center'),
        ),
        body: ListView.builder(
          itemCount: userMenuItems.length,
          itemBuilder: (context, index) {
            return Container(
              margin: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
              child: ClipRRect(
                borderRadius: BorderRadius.circular(8.0), // 圆角边框
                child: Material(
                  color: themeC.themeColor['bgc-primary'],
                  child: InkWell(
                    onTap: () {
                      // 处理点击事件
                      print('You tapped on ${userMenuItems[index]['text']}');
                    },
                    child: ListTile(
                      leading: Icon(userMenuItems[index]['icon']),
                      title: Text(userMenuItems[index]['text']),
                      trailing: Icon(Icons.keyboard_arrow_right), // 右边箭头
                    ),
                  ),
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}

TextButton ElevatedButton 等按钮尺寸大小

要设置 TextButton 的尺寸大小,你可以使用 ButtonStyle 中的 minimumSize 属性来指定按钮的最小尺寸。minimumSize 属性接受一个 Size 对象,表示按钮的最小宽度和高度。

dart
TextButton(
  onPressed: () {
    // 按钮点击事件
  },
  child: Text('TextButton'),
  style: ButtonStyle(
    minimumSize: MaterialStateProperty.all(Size(100.0, 50.0)), // 指定按钮的最小尺寸
  ),
)

ListView 循环,并且每项加背景和边框

要为 ListTile 添加背景颜色并在底部添加 1 像素的边框,你可以将 ListTile 包装在一个带有背景颜色和边框的 Container 中。以下是一个示例:

dart
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final List<String> items = ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5'];

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Array Loop Example'),
        ),
        body: ListView.builder(
          itemCount: items.length,
          itemBuilder: (context, index) {
            return Container(
              color: Colors.grey[200], // 设置背景颜色
              child: Column(
                children: [
                  ListTile(
                    title: Text(items[index]),
                  ),
                  Divider(
                    height: 1, // 设置分隔线高度
                    color: Colors.grey, // 设置分隔线颜色
                  ),
                ],
              ),
            );
          },
        ),
      ),
    );
  }
}

列表循环

通过判断条件输出不同的列表循环

dart

Container(
  constraints: const BoxConstraints(maxHeight: 200),
  child: SingleChildScrollView(
    physics: const AlwaysScrollableScrollPhysics(), // 允许内容滚动
    child: Wrap(
      spacing: 4,
      runSpacing: 10,
      children: type == 'normal'
          ? List.generate(4, (index) {
              return SizedBox(
                width: contentMaxWidth / 4 - 12,
                child: PrizeItem(),
              );
            }).toList()
          : List.generate(28, (index) {
              return SizedBox(
                width: contentMaxWidth / 4 - 12,
                child: PrizeItem(),
              );
            }).toList(),
    ),
  ),
),

InkWell 点击效果

需要放在 Material 里面

dart
Material(
    color: themeC.themeColor['bgc-primary'],
    child: InkWell(
      onTap: () {
        // 处理点击事件
        clickItem(userMenuItems[index]);
      },
      child: ListTile(
        contentPadding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 0.0),
        leading: Icon(userMenuItems[index]['icon']),
        title: Text(userMenuItems[index]['text']),
        trailing: Icon(Icons.keyboard_arrow_right), // 右边箭头
      ),
    ),
  ),

ScrollController 监听滚动到底部,加载更多数据

dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pc28/components/empty.dart';
import '../../locales/locale_controller.dart';
import '../../themes/themes_controller.dart';
import '../../components/my_app_bar.dart';
import '../../api/api.dart';
import '../game/game_num.dart';

class Bill extends StatefulWidget {
  const Bill({Key? key}) : super(key: key);

  @override
  State<Bill> createState() => _BillState();
}

class _BillState extends State<Bill> with SingleTickerProviderStateMixin {
  LocaleController localeC = Get.find<LocaleController>();
  ThemeController themeC = Get.find<ThemeController>();

  final ScrollController _scrollController = ScrollController();
  bool _isLoading = false;
  bool isLoaded = false;

  Map<String, dynamic> pageParams = {
    'page': 1,
    'limit': 20,
  };
  Map<String, dynamic> pageData = {
    'isNomore': false,
    'dataList': [],
  };

  void getData({bool? isMore, Function? callback}) async {
    final res = await apis['orderLog']!(pageParams);
    setState(() {
      isLoaded = true;
    });
    if (res['code'] == 1) {
      var resdata = res['data'] ?? {};
      if (resdata.isEmpty) return;
      if (isMore != null && !isMore) {
        if (mounted) {
          setState(() {
            pageData['dataList'] = resdata['data'];
          });
        }
      } else {
        if (mounted) {
          setState(() {
            pageData['dataList'] = [...pageData['dataList'], ...resdata['data']];
          });
        }
      }
      if (resdata['last_page'] > pageParams['page']) {
        setState(() {
          pageData['isNomore'] = false;
        });
      } else {
        setState(() {
          pageData['isNomore'] = true;
        });
      }
      if (callback != null) {
        callback();
      }
    }
  }

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(_scrollListener);
    getData();
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  void _scrollListener() {
    if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent) {
      // 滚动到底部
      if (!pageData['isNomore']) {
        _loadMoreData();
      }
    }
  }

  void _loadMoreData() {
    if (!_isLoading) {
      setState(() {
        _isLoading = true;
        ++pageParams['page'];
      });
      getData(
        isMore: true,
        callback: () {
          setState(() {
            _isLoading = false;
          });
        },
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    String getNum(types, nums) {
      String betdata = '';
      List typesList = types.split(',');
      List<Map<String, dynamic>> bets = normalNumbs.where((item) => typesList.contains(item['type'].toString())).toList();
      betdata = bets.map((numb) => numb['name'] as String).join(', ');
      if (typesList.contains('5')) {
        betdata = betdata == '' ? nums : '$betdata,$nums';
      }
      return betdata;
    }

    // String getStatus(status) {
    //   String statusText = '';
    //   if (status == 1) {
    //     statusText = 'Won'.tr;
    //   } else if (status == 2) {
    //     statusText = 'Didn_win'.tr;
    //   } else {
    //     statusText = 'Not_drawn_yet'.tr;
    //   }
    //   return statusText;
    // }
    String getCheck(status) {
      String statusText = '';
      if (status == 1) {
        statusText = 'Drawn_result'.tr;
      } else {
        statusText = 'Not_drawn_yet'.tr;
      }
      return statusText;
    }

    // String getSign(status) {
    //   String signText = '';
    //   if (status == 1) {
    //     signText = '+';
    //   } else if (status == 2) {
    //     signText = '';
    //   } else {
    //     signText = '';
    //   }
    //   return signText;
    // }

    return Scaffold(
      appBar: MyAppBar(
        title: 'financial_bill'.tr, // 将字符串包装在Text小部件中
        backgroundColor: themeC.themeColor['bgc-primary'],
      ),
      body: Column(
        children: [
          Container(
            height: 46.0,
            color: themeC.themeColor['bgc-primary'],
            padding: const EdgeInsets.symmetric(horizontal: 8.0),
            child: Row(
              children: [
                Expanded(
                  flex: 1,
                  child: Center(
                    child: Text(
                      'Issue'.tr,
                      style: const TextStyle(
                        fontSize: 14.0,
                      ),
                    ),
                  ),
                ),
                Expanded(
                  flex: 3,
                  child: Center(
                    child: Text(
                      'Betting_details'.tr,
                      style: const TextStyle(
                        fontSize: 14.0,
                      ),
                    ),
                  ),
                ),
                Expanded(
                  flex: 1,
                  child: Center(
                    child: Text(
                      'state'.tr,
                      style: const TextStyle(
                        fontSize: 14.0,
                      ),
                    ),
                  ),
                ),
                Expanded(
                  flex: 2,
                  child: Center(
                    child: Text(
                      'Profit_and_loss'.tr,
                      style: const TextStyle(
                        fontSize: 14.0,
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ),
          isLoaded && pageData['dataList'].isEmpty
              ? const Expanded(child: Empty())
              : Expanded(
                  child: ListView.builder(
                    controller: _scrollController,
                    shrinkWrap: true, // 防止与SingleChildScrollView冲突
                    itemCount: pageData['dataList'].length + (_isLoading ? 1 : 0),
                    itemBuilder: (BuildContext content, int index) {
                      if (index < pageData['dataList'].length) {
                        var item = pageData['dataList'][index];
                        var betStr = getNum(item['buy_type'], item['buy_num']);
                        return Container(
                          margin: const EdgeInsets.symmetric(horizontal: 8.0),
                          padding: const EdgeInsets.symmetric(
                            vertical: 10.0,
                          ),
                          decoration: BoxDecoration(
                            border: Border(
                              bottom: BorderSide(
                                width: 1.0,
                                color: themeC.themeColor['bd-base']!,
                              ),
                            ),
                          ),
                          child: Row(
                            children: [
                              Expanded(
                                flex: 1,
                                child: Center(
                                  child: Text(
                                    '${item['code_sn']}',
                                    style: TextStyle(
                                      fontSize: 12.0,
                                      fontWeight: FontWeight.w700,
                                      color: themeC.themeColor['c-text-2'],
                                    ),
                                  ),
                                ),
                              ),
                              Expanded(
                                flex: 3,
                                child: Column(
                                  children: [
                                    RichText(
                                      textAlign: TextAlign.center,
                                      text: TextSpan(
                                          style: TextStyle(
                                            color: themeC.themeColor['c-text-3'],
                                          ),
                                          children: [
                                            TextSpan(
                                              text: '$betStr/',
                                              style: const TextStyle(
                                                fontSize: 10,
                                              ),
                                            ),
                                            TextSpan(
                                              text: '${item['buy_amount_one']}',
                                              style: TextStyle(
                                                color: themeC.themeColor['c-price'],
                                              ),
                                            ),
                                            const TextSpan(
                                              text: 'VND',
                                              style: TextStyle(
                                                fontSize: 11,
                                              ),
                                            ),
                                          ]),
                                    ),
                                    const SizedBox(
                                      height: 5,
                                    ),
                                    RichText(
                                      textAlign: TextAlign.center,
                                      text: TextSpan(
                                          style: TextStyle(
                                            color: themeC.themeColor['c-text-2'],
                                            fontWeight: FontWeight.w700,
                                          ),
                                          children: [
                                            TextSpan(
                                              text: '${'total'.tr}:',
                                              style: const TextStyle(
                                                fontSize: 10,
                                              ),
                                            ),
                                            TextSpan(
                                              text: '${item['buy_amount']}',
                                              style: TextStyle(
                                                color: themeC.themeColor['c-price'],
                                              ),
                                            ),
                                            const TextSpan(
                                              text: 'VND',
                                              style: TextStyle(
                                                fontSize: 11,
                                              ),
                                            ),
                                          ]),
                                    ),
                                  ],
                                ),
                              ),
                              Expanded(
                                flex: 1,
                                child: Center(
                                  child: Text(
                                    getCheck(item['is_check']),
                                    style: TextStyle(
                                      color: item['is_check'] == 1 ? themeC.themeColor['c-fall'] : themeC.themeColor['c-rise'],
                                    ),
                                  ),
                                ),
                              ),
                              Expanded(
                                flex: 2,
                                child: Center(
                                  child: Text(
                                    '${num.parse(item['earn_lost']) >= 0 ? '+' + item['earn_lost'] : item['earn_lost']}',
                                    style: TextStyle(
                                      color: num.parse(item['earn_lost']) >= 0 ? themeC.themeColor['c-fall'] : themeC.themeColor['c-rise'],
                                    ),
                                  ),
                                ),
                              ),
                            ],
                          ),
                        );
                      } else {
                        if (pageData['isNomore']) {
                          return const SizedBox();
                        } else {
                          return Padding(
                            padding: const EdgeInsets.symmetric(vertical: 16.0),
                            child: Center(
                              child: SizedBox(
                                width: 24,
                                height: 24,
                                child: CircularProgressIndicator(
                                  color: Colors.grey.withOpacity(0.5),
                                  strokeWidth: 3,
                                ),
                              ), // 加载指示器
                            ),
                          );
                        }
                      }
                    },
                  ),
                ),
        ],
      ),
    );
  }
}

dart 数组操作

创建数组

  • 使用 List 字面量创建数组
dart
List<int> numbers = [1, 2, 3, 4, 5];
  • 使用 List 构造函数创建数组
dart
List<String> fruits = List<String>();
fruits.add('Apple');
fruits.add('Banana');
fruits.add('Orange');

获取数组长度

dart
List<int> numbers = [1, 2, 3, 4, 5];
int length = numbers.length;
print('Length of numbers: $length');

访问数组元素

dart
List<String> fruits = ['Apple', 'Banana', 'Orange'];
String firstFruit = fruits[0];
print('First fruit: $firstFruit');

添加元素到数组

dart
List<String> fruits = ['Apple', 'Banana', 'Orange'];
fruits.add('Mango');
print('Updated fruits: $fruits');

移除数组中的元素

dart
List<int> numbers = [1, 2, 3, 4, 5];
numbers.remove(3); // 移除元素 3
print('Updated numbers: $numbers');

遍历数组

  • 使用 for 循环
dart
List<int> numbers = [1, 2, 3, 4, 5];
for (int i = 0; i < numbers.length; i++) {
  print('Element at index $i: ${numbers[i]}');
}
  • 使用 forEach 方法
dart
List<String> fruits = ['Apple', 'Banana', 'Orange'];
fruits.forEach((fruit) {
  print('Fruit: $fruit');
});

转换数组

  • 将数组转换为字符串
dart
List<String> fruits = ['Apple', 'Banana', 'Orange'];
String fruitsStr = fruits.join(', ');
print('Fruits: $fruitsStr');
  • 将数组中的元素映射到新的数组
dart
List<int> numbers = [1, 2, 3, 4, 5];
List<String> numberStrs = numbers.map((number) => number.toString()).toList();
print('Number strings: $numberStrs');

检查数组是否包含某个元素

dart
List<String> fruits = ['Apple', 'Banana', 'Orange'];
bool containsBanana = fruits.contains('Banana');
print('Contains Banana? $containsBanana');

一个数组的某个标识包含另外一个数组的值

要判断 normalNumbs 列表中 type 字段的值是否在 [2, 3] 中,可以使用 where() 方法来过滤列表,然后使用 any() 方法来检查是否存在符合条件的元素。以下是示例代码:

dart
void main() {
  List<Map<String, dynamic>> normalNumbs = [
    {'type': 1, 'name': '大', 'rate': 'big_rate', 'check': false},
    {'type': 2, 'name': '小', 'rate': 'small_rate', 'check': false},
    {'type': 3, 'name': '单', 'rate': 'd_rate', 'check': false},
    {'type': 4, 'name': '双', 'rate': 's_rate', 'check': false}
  ];

  List<Map<String, dynamic>> filteredItems = normalNumbs.where((item) => [2, 3].contains(item['type'])).toList();

  print(filteredItems); // 输出包含指定类型的项目列表
}

在这个示例中,where() 方法将用于过滤列表,只保留 type 字段的值在 [2, 3] 中的元素。然后,isNotEmpty 属性将用于检查过滤后的列表是否包含任何元素,如果列表不为空,则表示 type 字段的值在 [2, 3] 中。

将数组的值复制到另一个数组

dart
List<int> originalList = [1, 2, 3, 4, 5];
List<int> copiedList = List<int>.from(originalList);
print('Copied list: $copiedList');
dart
List<Map<String, dynamic>> normalNumbs = [
  {'type': 1, 'name': '大', 'rate': 'big_rate', 'check': false},
  {'type': 2, 'name': '小', 'rate': 'small_rate', 'check': false},
  {'type': 3, 'name': '单', 'rate': 'd_rate', 'check': false},
  {'type': 4, 'name': '双', 'rate': 's_rate', 'check': false}
];
List<Map<String, dynamic>> normalList = List.from(normalNumbs.map((element) => {...element}));
List<Map<String, dynamic>> specialList = List.from(specialNumbs.map((element) => {...element}));

筛选数组中满足条件的元素

dart
List<int> numbers = [1, 2, 3, 4, 5];
List<int> evenNumbers = numbers.where((number) => number % 2 == 0).toList();
print('Even numbers: $evenNumbers');

对数组进行排序

dart
List<int> numbers = [5, 2, 8, 1, 3];
numbers.sort();
print('Sorted numbers: $numbers');

将数组拆分为多个部分

dart
List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
List<List<int>> chunks = [];
int chunkSize = 3;
for (int i = 0; i < numbers.length; i += chunkSize) {
  chunks.add(numbers.sublist(i, i + chunkSize));
}
print('Chunks: $chunks');

合并多个数组

dart
List<int> list1 = [1, 2, 3];
List<int> list2 = [4, 5, 6];
List<int> mergedList = [...list1, ...list2];
print('Merged list: $mergedList');

数组中的重复项计数

dart
List<int> numbers = [1, 2, 3, 2, 4, 3, 2, 5, 2];
Map<int, int> countMap = {};
numbers.forEach((number) {
  countMap[number] = (countMap[number] ?? 0) + 1;
});
print('Count map: $countMap');

扁平化多维数组

dart
List<List<int>> matrix = [
  [1, 2, 3],
  [4, 5],
  [6, 7, 8]
];
List<int> flatList = [];
matrix.forEach((row) {
  flatList.addAll(row);
});
print('Flat list: $flatList');

按条件分组数组元素

dart
List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
Map<bool, List<int>> groupedMap = numbers.groupBy((number) => number % 2 == 0);
print('Grouped map: $groupedMap');

转置矩阵

dart
List<List<int>> matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];
List<List<int>> transposedMatrix = List.generate(matrix[0].length, (colIndex) {
  return List.generate(matrix.length, (rowIndex) => matrix[rowIndex][colIndex]);
});
print('Transposed matrix: $transposedMatrix');

滚动到底部触发加载更多

dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../locales/locale_controller.dart';
import '../../themes/themes_controller.dart';
import '../../components/my_app_bar.dart';
import '../../api/api.dart';
import '../game/game_num.dart';

class Bill extends StatefulWidget {
  const Bill({Key? key}) : super(key: key);

  @override
  State<Bill> createState() => _BillState();
}

class _BillState extends State<Bill> with SingleTickerProviderStateMixin {
  LocaleController localeC = Get.find<LocaleController>();
  ThemeController themeC = Get.find<ThemeController>();

  final ScrollController _scrollController = ScrollController();
  bool _isLoading = false;

  Map<String, dynamic> pageData = {
    'page': 1,
    'limit': 20,
    'isNomore': false,
    'dataList': [],
  };

  void getData({bool? isMore, Function? callback}) async {
    final res = await apis['orderLog']!(pageData);
    if (res['code'] == 1) {
      var resdata = res['data'] ?? {};
      if (resdata.isEmpty) return;
      if (isMore != null && !isMore) {
        if (mounted) {
          setState(() {
            pageData['dataList'] = resdata['data'];
          });
        }
      } else {
        if (mounted) {
          setState(() {
            pageData['dataList'] = [...pageData['dataList'], ...resdata['data']];
          });
        }
      }
      if (resdata['last_page'] > pageData['page']) {
        pageData['isNomore'] = false;
      } else {
        pageData['isNomore'] = true;
      }
      callback ?? ();
    }
  }

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(_scrollListener);
    getData();
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  void _scrollListener() {
    if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) {
      // 滚动到底部
      if (!pageData['isNomore']) {
        _loadMoreData();
      }
    }
  }

  void _loadMoreData() {
    if (!_isLoading) {
      setState(() {
        _isLoading = true;
        ++pageData['page'];
      });
      getData(
        isMore: true,
        callback: () {
          _isLoading = false;
        },
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    String getNum(types, nums) {
      String betdata = '';
      List typesList = types.split(',');
      List<Map<String, dynamic>> bets = normalNumbs.where((item) => typesList.contains(item['type'].toString())).toList();
      betdata = bets.map((numb) => numb['name'] as String).join(', ');
      if (typesList.contains('5')) {
        betdata = betdata == '' ? nums : '$betdata,$nums';
      }
      return betdata;
    }

    String getStatus(status) {
      String statusText = '';
      if (status == 1) {
        statusText = '已中奖';
      } else if (status == 2) {
        statusText = '未中奖';
      } else {
        statusText = '未开奖';
      }
      return statusText;
    }

    String getSign(status) {
      String signText = '';
      if (status == 1) {
        signText = '+';
      } else if (status == 2) {
        signText = '-';
      } else {
        signText = '';
      }
      return signText;
    }

    return Scaffold(
      appBar: MyAppBar(
        title: '财务账单', // 将字符串包装在Text小部件中
        backgroundColor: themeC.themeColor['bgc-primary'],
      ),
      body: Column(
        children: [
          Container(
            height: 46.0,
            color: themeC.themeColor['bgc-primary'],
            padding: const EdgeInsets.symmetric(horizontal: 8.0),
            child: const Row(
              children: [
                Expanded(
                  flex: 1,
                  child: Center(
                    child: Text(
                      '期号',
                      style: TextStyle(
                        fontSize: 14.0,
                      ),
                    ),
                  ),
                ),
                Expanded(
                  flex: 3,
                  child: Center(
                    child: Text(
                      '下注详情',
                      style: TextStyle(
                        fontSize: 14.0,
                      ),
                    ),
                  ),
                ),
                Expanded(
                  flex: 1,
                  child: Center(
                    child: Text(
                      '状态',
                      style: TextStyle(
                        fontSize: 14.0,
                      ),
                    ),
                  ),
                ),
                Expanded(
                  flex: 2,
                  child: Center(
                    child: Text(
                      '盈亏',
                      style: TextStyle(
                        fontSize: 14.0,
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ),
          Expanded(
            child: ListView.builder(
              controller: _scrollController,
              shrinkWrap: true, // 防止与SingleChildScrollView冲突
              itemCount: pageData['dataList'].length + (_isLoading ? 1 : 0),
              itemBuilder: (BuildContext content, int index) {
                if (index < pageData['dataList'].length) {
                  var item = pageData['dataList'][index];
                  var betStr = getNum(item['buy_type'], item['buy_num']);
                  return Container(
                    margin: const EdgeInsets.symmetric(horizontal: 8.0),
                    padding: const EdgeInsets.symmetric(
                      vertical: 10.0,
                    ),
                    decoration: BoxDecoration(
                      border: Border(
                        bottom: BorderSide(
                          width: 1.0,
                          color: themeC.themeColor['bd-base']!,
                        ),
                      ),
                    ),
                    child: Row(
                      children: [
                        Expanded(
                          flex: 1,
                          child: Center(
                            child: Text(
                              '${item['code_sn']}',
                              style: TextStyle(
                                fontSize: 12.0,
                                color: themeC.themeColor['c-text-3'],
                              ),
                            ),
                          ),
                        ),
                        Expanded(
                          flex: 3,
                          child: Center(
                            child: RichText(
                              text: TextSpan(
                                  style: TextStyle(
                                    color: themeC.themeColor['c-text-2'],
                                  ),
                                  children: [
                                    TextSpan(
                                      text: '$betStr/',
                                      style: const TextStyle(
                                        fontSize: 10,
                                      ),
                                    ),
                                    TextSpan(
                                      text: '${item['buy_amount_one']}',
                                      style: TextStyle(
                                        color: themeC.themeColor['c-price'],
                                      ),
                                    ),
                                    const TextSpan(
                                      text: 'VND',
                                      style: TextStyle(
                                        fontSize: 12,
                                      ),
                                    ),
                                  ]),
                            ),
                          ),
                        ),
                        Expanded(
                          flex: 1,
                          child: Center(
                            child: Text(
                              getStatus(item['status']),
                              style: TextStyle(
                                color: item['status'] == 1 ? themeC.themeColor['c-fall'] : themeC.themeColor['c-rise'],
                              ),
                            ),
                          ),
                        ),
                        Expanded(
                          flex: 2,
                          child: Center(
                            child: Text(
                              '${getSign(item['status'])}${item['earn_lost']}',
                              style: TextStyle(
                                color: item['status'] == 1 ? themeC.themeColor['c-fall'] : themeC.themeColor['c-rise'],
                              ),
                            ),
                          ),
                        ),
                      ],
                    ),
                  );
                } else {
                  if (pageData['isNomore']) {
                    return const SizedBox();
                  } else {
                    return Padding(
                      padding: const EdgeInsets.symmetric(vertical: 16.0),
                      child: Center(
                        child: SizedBox(
                          width: 24,
                          height: 24,
                          child: CircularProgressIndicator(
                            color: Colors.grey.withOpacity(0.5),
                            strokeWidth: 3,
                          ),
                        ), // 加载指示器
                      ),
                    );
                  }
                }
              },
            ),
          ),
        ],
      ),
    );
  }
}

判断 web 平台引入 dart:html 包

dart
import 'package:pc28/utils/file_to_base64.dart' if (dart.library.html) 'package:pc28/utils/file_to_base64_web.dart';

文字描边效果

dart
Stack(
  children: <Widget>[
    // 文字描边
    Text(
      '文字描边',
      style: TextStyle(
        fontSize: 20,
        fontWeight: FontWeight.w700,
        foreground: Paint()
          ..style = PaintingStyle.stroke
          ..strokeWidth = 4
          ..color = Colors.white,
      ),
    ),
    // 实际文字
    Text(
      '文字描边',
      style: const TextStyle(
        fontSize: 20,
        fontWeight: FontWeight.w700,
        color: Color(0xff0080DD), // 或者你想要的文字颜色
      ),
    ),
  ],
)

弹窗里面有列表切换,有表单操作

dart
void showBuy(BuildContext context) {
    showModalBottomSheet(
      isScrollControlled: true,
      isDismissible: true,
      elevation: 10.0,
      context: context,
      useSafeArea: true,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(0)),
      ),
      builder: (BuildContext context) {
        return StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
          return SingleChildScrollView(
            child: LayoutBuilder(
              builder: (BuildContext context, BoxConstraints constraints) {
                double contentMaxWidth = constraints.maxWidth;
                return GestureDetector(
                  behavior: HitTestBehavior.translucent,
                  onTap: () {
                    FocusScope.of(context).requestFocus(FocusNode());
                  },
                  child: Container(
                    padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 15.0),
                    child: Column(
                      children: [
                        Row(
                          children: [
                            Expanded(
                              flex: 1,
                              child: Center(
                                child: ElevatedButton(
                                  onPressed: () {
                                    setState(() {
                                      type = 'normal';
                                    });
                                  },
                                  style: ButtonStyle(
                                    backgroundColor: MaterialStatePropertyAll(type == 'normal' ? themeC.themeColor['c-second'] : themeC.themeColor['c-primary']),
                                    shape: MaterialStatePropertyAll(
                                      RoundedRectangleBorder(
                                        borderRadius: BorderRadius.circular(4.0),
                                      ),
                                    ),
                                  ),
                                  child: Text(
                                    'ordinary'.tr,
                                    style: const TextStyle(fontSize: 16.0),
                                  ),
                                ),
                              ),
                            ),
                            Expanded(
                              flex: 1,
                              child: Center(
                                child: ElevatedButton(
                                  onPressed: () {
                                    setState(() {
                                      type = 'special';
                                    });
                                  },
                                  style: ButtonStyle(
                                    backgroundColor: MaterialStatePropertyAll(type == 'special' ? themeC.themeColor['c-second'] : themeC.themeColor['c-primary']),
                                    shape: MaterialStatePropertyAll(
                                      RoundedRectangleBorder(
                                        borderRadius: BorderRadius.circular(4.0),
                                      ),
                                    ),
                                  ),
                                  child: Text(
                                    'special'.tr,
                                    style: const TextStyle(fontSize: 16.0),
                                  ),
                                ),
                              ),
                            ),
                          ],
                        ),
                        const Divider(
                          height: 14,
                          color: Colors.black26,
                        ),
                        Container(
                          constraints: const BoxConstraints(maxHeight: 200),
                          child: SingleChildScrollView(
                            physics: const AlwaysScrollableScrollPhysics(), // 允许内容滚动
                            child: Wrap(
                              spacing: 4,
                              runSpacing: 10,
                              children: type == 'normal'
                                  ? normalList.map((item) {
                                      int index = normalList.indexOf(item);
                                      return SizedBox(
                                        width: contentMaxWidth / 4 - 12,
                                        height: 28,
                                        child: Material(
                                          color: item['check'] ? themeC.themeColor['c-second'].withOpacity(0.8) : themeC.themeColor['bgc-primary'],
                                          borderRadius: BorderRadius.circular(4.0),
                                          child: InkWell(
                                            onTap: () {
                                              setState(() {
                                                normalList[index]['check'] = !normalList[index]['check'];
                                              });
                                            },
                                            child: PrizeItem(config: codeConfig, data: item),
                                          ),
                                        ),
                                      );
                                    }).toList()
                                  : specialList.map((item) {
                                      int index = specialList.indexOf(item);
                                      return SizedBox(
                                        width: contentMaxWidth / 4 - 12,
                                        height: 28,
                                        child: Material(
                                          color: item['check'] ? themeC.themeColor['c-second'].withOpacity(0.8) : themeC.themeColor['bgc-primary'],
                                          borderRadius: BorderRadius.circular(4.0),
                                          child: InkWell(
                                            onTap: () {
                                              setState(() {
                                                specialList[index]['check'] = !specialList[index]['check'];
                                              });
                                            },
                                            child: PrizeItem(config: codeConfig, data: item),
                                          ),
                                        ),
                                      );
                                    }).toList(),
                            ),
                          ),
                        ),
                        Padding(
                          padding: const EdgeInsets.only(top: 12, bottom: 12, left: 4, right: 4),
                          child: Wrap(
                            children: [
                              Container(
                                margin: const EdgeInsets.all(4),
                                height: 26,
                                decoration: BoxDecoration(
                                  border: Border.all(
                                    width: 1,
                                    color: themeC.themeColor['c-primary'],
                                  ),
                                  borderRadius: BorderRadius.circular(4),
                                ),
                                child: TextButton(
                                  onPressed: () {
                                    Get.toNamed('/rule');
                                  },
                                  child: Text('Rule_description'.tr),
                                ),
                              ),
                              Container(
                                margin: const EdgeInsets.all(4),
                                height: 26,
                                decoration: BoxDecoration(
                                  border: Border.all(
                                    width: 1,
                                    color: themeC.themeColor['c-primary'],
                                  ),
                                  borderRadius: BorderRadius.circular(4),
                                ),
                                child: TextButton(
                                  onPressed: () {
                                    rootC.changePageIndex(1);
                                    Get.toNamed('/root');
                                  },
                                  child: Text('Online_deposit'.tr),
                                ),
                              ),
                              Container(
                                margin: const EdgeInsets.all(4),
                                height: 26,
                                decoration: BoxDecoration(
                                  border: Border.all(
                                    width: 1,
                                    color: themeC.themeColor['c-primary'],
                                  ),
                                  borderRadius: BorderRadius.circular(4),
                                ),
                                child: TextButton(
                                  onPressed: () {
                                    Get.toNamed('/withdraw');
                                  },
                                  child: Text('Quick_cash_withdrawal'.tr),
                                ),
                              ),
                              Container(
                                margin: const EdgeInsets.all(4),
                                height: 26,
                                decoration: BoxDecoration(
                                  border: Border.all(
                                    width: 1,
                                    color: themeC.themeColor['c-primary'],
                                  ),
                                  borderRadius: BorderRadius.circular(4),
                                ),
                                child: TextButton(
                                  onPressed: () {
                                    Get.toNamed('/bill');
                                  },
                                  child: Text('Betting_records'.tr),
                                ),
                              ),
                            ],
                          ),
                        ),
                        Row(
                          children: [
                            Text('${'Buy_points'.tr}:'),
                            Expanded(
                              child: Container(
                                  height: 40.0,
                                  padding: const EdgeInsets.symmetric(horizontal: 6.0),
                                  child: MyTextfield(
                                    maxLength: 10,
                                    backgroundColor: Colors.white,
                                    keyboardType: ITextInputType.number,
                                    hintText: 'Please_enter_purchase_points'.tr,
                                    controlCallBack: (control) {
                                      numControl = control;
                                    },
                                    fieldCallBack: (val) {
                                      formData['buy_amount_one'] = val;
                                    },
                                  )),
                            ),
                            ElevatedButton(
                              onPressed: () {
                                buyOrder(context);
                              },
                              style: ButtonStyle(
                                backgroundColor: MaterialStatePropertyAll(themeC.themeColor['c-second']),
                                shape: MaterialStatePropertyAll(
                                  RoundedRectangleBorder(
                                    borderRadius: BorderRadius.circular(4.0),
                                  ),
                                ),
                              ),
                              child: Text('Buy'.tr),
                            ),
                          ],
                        ),
                      ],
                    ),
                  ),
                );
              },
            ),
          );
        });
      },
    );
  }
}

点击展开收缩图片旋转动画

dart

bool _isExpanded = false;

TweenAnimationBuilder(
  duration: const Duration(milliseconds: 300),
  tween: Tween<double>(begin: _isExpanded ? -1 : 0.0, end: _isExpanded ? 1.0 : 0.0),
  builder: (context, double value, child) {
    return Transform.rotate(
      angle: value * -1.0 * 3.14,
      child: const MyImage(
        url: 'assets/images/icon_arrow_down.png',
        width: 18,
        height: 18,
      ),
    );
  },
)

多颜色渐变列表

dart
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart';
import 'package:pc28/components/my_app_bar.dart';
import '../../api/api.dart';
import '../../auth/auth_controller.dart';

class Room extends StatefulWidget {
  const Room({Key? key}) : super(key: key);

  @override
  State<Room> createState() => _RoomState();
}

class _RoomState extends State<Room> {
  AuthController authC = Get.find<AuthController>();

  List listColors = [
    [const Color(0xff7B8DB1), const Color(0xff748DB2), const Color(0xff8B8DCA)],
    [const Color(0xffE59343), const Color(0xffF09C39), const Color(0xffFF7E36)],
    [const Color(0xffF3C095), const Color(0xffDC7E79), const Color(0xffEAA9CB)],
    [const Color(0xffE0E0FB), const Color(0xffC5CDF5), const Color(0xffF6D3DA)],
  ];
  List listImgs = [
    'assets/images/room_1.png',
    'assets/images/room_2.png',
    'assets/images/room_3.png',
    'assets/images/room_4.png',
  ];

  List list = [];
  void getRoomList() async {
    final res = await apis['colorsCodeConfig']!(<String, dynamic>{});
    if (res['code'] == 1) {
      setState(() {
        list = res['data'] ?? [];
      });
    }
  }

  void toGame(data) {
    if (num.parse(data['min_one']) > num.parse(authC.userinfo['money'])) {
      EasyLoading.showToast('${'The_balance_cannot_be_less_than'.tr} ${data['min_one']}');
      return;
    }
    Get.toNamed(
      '/game',
      arguments: data,
    );
  }

  @override
  void initState() {
    super.initState();
    getRoomList();
  }

  List<Widget> getRooms() {
    List<Widget> rooms = [];
    for (var i = 0; i < list.length; i++) {
      rooms.add(Container(
        margin: const EdgeInsets.symmetric(vertical: 6.0),
        padding: const EdgeInsets.only(top: 45.0, left: 30.0),
        width: double.maxFinite,
        height: 150.0,
        decoration: BoxDecoration(
          image: DecorationImage(
            image: AssetImage(listImgs[i % 4]),
            fit: BoxFit.fill,
          ),
        ),
        child: InkWell(
          onTap: () {
            toGame(list[i]);
          },
          highlightColor: Colors.transparent,
          splashColor: Colors.transparent,
          child: ShaderMask(
            shaderCallback: (Rect bounds) {
              return LinearGradient(
                begin: Alignment.centerLeft,
                end: Alignment.centerRight,
                colors: listColors[i % 4],
                tileMode: TileMode.mirror,
              ).createShader(bounds);
            },
            child: Text(
              list[i]['colors_name'],
              style: const TextStyle(
                fontSize: 20,
                fontWeight: FontWeight.w700,
                color: Colors.white, // 或者你想要的文字颜色
              ),
            ),
          ),
        ),
      ));
    }
    return rooms;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: MyAppBar(
        title: 'Select_room'.tr,
      ),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.only(top: 6.0, left: 16.0, right: 16.0, bottom: 6.0),
          child: Column(
            children: getRooms(),
          ),
        ),
      ),
    );
  }
}