flutter 使列表可重新排序

6mw9ycah  于 2023-05-29  发布在  Flutter
关注(0)|答案(1)|浏览(200)

我有一个小部件列表(练习)在我的屏幕上。我想让它使练习是可重新排序的,这样我就可以拖放它们,顺序就会改变。
这是我的完整代码:

import 'dart:collection';

import 'package:flutter/material.dart';
import 'package:workout_app/Screens/Components/Widgets/footer.dart';

class workout_page extends StatefulWidget {
  @override
  _WorkoutPageState createState() => _WorkoutPageState();
}

class _WorkoutPageState extends State<workout_page>
    with TickerProviderStateMixin {
  late TabController _tabController;
  late PageController _pageController;
  TextEditingController _newTabController = TextEditingController();
  late var currentPageIndx;

  List<String> plans = [];
  late String firstplan;

  List<Map<String, dynamic>> workoutMap = [
    {
      'plan_name': 'My First Plan',
      'content': [
        {
          'name': 'My First Workout',
          'exercises': [
            {
              'name': 'Bicep Curl',
              'musclesworked': ['bicep', 'tricep']
            },
            {
              'name': 'Preacher Curl',
              'musclesworked': ['bicep', 'tricep']
            },
            {
              'name': 'Bicep Curl',
              'musclesworked': ['bicep', 'tricep']
            },
            {
              'name': 'Preacher Curl',
              'musclesworked': ['bicep', 'tricep']
            },
            {
              'name': 'Bicep Curl',
              'musclesworked': ['bicep', 'tricep']
            },
            {
              'name': 'Preacher Curl',
              'musclesworked': ['bicep', 'tricep']
            },
            {
              'name': 'Bicep Curl',
              'musclesworked': ['bicep', 'tricep']
            },
            {
              'name': 'Preacher Curl',
              'musclesworked': ['bicep', 'tricep']
            }
          ]
        }
      ]
    },
    {
      'plan_name': 'My Second Plan',
      'content': [
        {
          'name': 'My Second Workout',
          'exercises': [
            {
              'name': 'POOPY Curl',
              'musclesworked': ['bicep', 'tricep']
            },
            {
              'name': 'Preacher Curl',
              'musclesworked': ['bicep', 'tricep']
            },
            {
              'name': 'Bicep Curl',
              'musclesworked': ['bicep', 'tricep']
            },
            {
              'name': 'Preacher Curl',
              'musclesworked': ['bicep', 'tricep']
            },
            {
              'name': 'Bicep Curl',
              'musclesworked': ['bicep', 'tricep']
            },
            {
              'name': 'Preacher Curl',
              'musclesworked': ['bicep', 'tricep']
            },
            {
              'name': 'Bicep Curl',
              'musclesworked': ['bicep', 'tricep']
            },
            {
              'name': 'Preacher Curl',
              'musclesworked': ['bicep', 'tricep']
            }
          ]
        }
      ]
    },
  ];

  @override
  void initState() {
    super.initState();
    for (var i = 0; i < workoutMap.length; i++) {
      plans.add(workoutMap[i]['plan_name']);
    }
    firstplan = plans[0];
    currentPageIndx = 1;
    _tabController = TabController(
      length: workoutMap[currentPageIndx]['content'].length +
          (workoutMap[currentPageIndx]['content'].length < 4 ? 1 : 0),
      vsync: this,
    );
    _pageController = PageController();
  }

  void _addTab() {
    setState(() {
      String newTabName = _newTabController.text;
      Map<String, Object> newTab = {
        'name': newTabName,
        'exercises': [],
      };
      workoutMap[currentPageIndx]['content'].add(newTab);
      _newTabController.clear();
      _tabController = TabController(
        length: workoutMap[currentPageIndx]['content'].length +
            (workoutMap[currentPageIndx]['content'].length < 4 ? 1 : 0),
        vsync: this,
      );
    });
  }

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

  @override
  Widget build(BuildContext context) {
    var size = MediaQuery.of(context).size;
    return Container(
      child: Material(
        color: Colors.green,
        child: DefaultTabController(
          length: workoutMap[currentPageIndx]['content'].length +
              (workoutMap[currentPageIndx]['content'].length < 4 ? 1 : 0),
          child: Scaffold(
            resizeToAvoidBottomInset: false,
            backgroundColor: Color.fromRGBO(79, 79, 79, 1),
            body: Stack(
              children: [
                CustomScrollView(
                  slivers: [
                    SliverAppBar(
                      titleSpacing: 15,
                      pinned: true,
                      elevation: 0,
                      backgroundColor: Color.fromARGB(255, 17, 17, 17),
                      centerTitle: false,
                      bottom: PreferredSize(
                        preferredSize: const Size.fromHeight(40),
                        child: Column(
                          children: [
                            Container(
                              color: Color.fromARGB(255, 17, 17, 17),
                              child: Row(
                                children: [
                                  Spacer(),
                                  Container(
                                    padding: EdgeInsets.symmetric(
                                        horizontal:
                                            10.0), // 1. Padding on the left and right sides
                                    decoration: BoxDecoration(
                                      color: Color.fromARGB(255, 35, 35, 35),
                                      borderRadius: BorderRadius.circular(
                                          3.0), // 2. Circular edges
                                    ),
                                    child: ConstrainedBox(
                                      // To3make the entire container smaller
                                      constraints: BoxConstraints(
                                          maxWidth: size.width * .5,
                                          maxHeight: 35),
                                      child: DropdownButtonHideUnderline(
                                        child: DropdownButton<String>(
                                          value: firstplan,
                                          dropdownColor:
                                              Color.fromARGB(255, 35, 35, 35),
                                          isExpanded:
                                              true, // To make sure dropdown appears below
                                          items: plans
                                              .map(
                                                (item) =>
                                                    DropdownMenuItem<String>(
                                                  value: item,
                                                  child: Text(
                                                    item,
                                                    style: TextStyle(
                                                      fontSize: 15,
                                                      color: Colors.white,
                                                    ),
                                                  ),
                                                ),
                                              )
                                              .toList(),
                                          onChanged: (item) {
                                            setState(() {
                                              firstplan = item!;
                                              currentPageIndx =
                                                  plans.indexOf(firstplan);
                                              _tabController = TabController(
                                                length: workoutMap[
                                                                currentPageIndx]
                                                            ['content']
                                                        .length +
                                                    (workoutMap[currentPageIndx]
                                                                    ['content']
                                                                .length <
                                                            4
                                                        ? 1
                                                        : 0),
                                                vsync: this,
                                              );
                                            });
                                          },
                                        ),
                                      ),
                                    ),
                                  ),
                                  SizedBox(width: 10),
                                  Transform.translate(
                                    offset: Offset(0, .3),
                                    child: ElevatedButton(
                                      onPressed: () => print('premium'),
                                      style: ElevatedButton.styleFrom(
                                        primary:
                                            Color.fromARGB(255, 35, 35, 35),
                                      ),
                                      child: Text('Go Premium'),
                                    ),
                                  ),
                                  Spacer()
                                ],
                              ),
                            ),
                            Container(
                              decoration: BoxDecoration(
                                color: Color.fromRGBO(60, 60, 60, 1),
                              ),
                              child: TabBar(
                                indicatorColor: Colors.grey,
                                controller: _tabController,
                                onTap: (index) {
                                  if (index == _tabController.length - 1 &&
                                      workoutMap[currentPageIndx]['content']
                                              .length <
                                          4) {
                                    _tabController.animateTo(
                                        _tabController.previousIndex);
                                  } else {
                                    _tabController.animateTo(index);
                                    _pageController.jumpToPage(index);
                                  }
                                },
                                tabs: [
                                  ...workoutMap[currentPageIndx]['content'].map(
                                    (tab) => Tab(
                                      text: tab['name'] as String,
                                    ),
                                  ),
                                  if (workoutMap[currentPageIndx]['content']
                                          .length <
                                      4)
                                    Container(
                                      width: 40,
                                      height: 40,
                                      alignment: Alignment.center,
                                      child: IconButton(
                                        icon: Icon(Icons.add),
                                        onPressed: () {
                                          showDialog(
                                            context: context,
                                            builder: (context) => AlertDialog(
                                              title: Text('Add Tab'),
                                              content: TextField(
                                                controller: _newTabController,
                                                decoration: InputDecoration(
                                                  hintText: 'Enter tab name',
                                                ),
                                              ),
                                              actions: [
                                                TextButton(
                                                  child: Text('Cancel'),
                                                  onPressed: () {
                                                    Navigator.pop(context);
                                                  },
                                                ),
                                                TextButton(
                                                  child: Text('Add'),
                                                  onPressed: () {
                                                    _addTab();
                                                    Navigator.pop(context);
                                                  },
                                                ),
                                              ],
                                            ),
                                          );
                                        },
                                      ),
                                    ),
                                ],
                              ),
                            ),
                          ],
                        ),
                      ),
                    ),
                    SliverToBoxAdapter(
                      child: SizedBox(
                        height: size.height - 170,
                        child: PageView(
                          controller: _pageController,
                          onPageChanged: (index) {
                            if (index == _tabController.length - 1 &&
                                workoutMap[currentPageIndx]['content'].length <
                                    4) {
                              _pageController.jumpToPage(_tabController.index);
                            } else {
                              _tabController.animateTo(index);
                            }
                          },
                          children: [
                            ...workoutMap[currentPageIndx]['content'].map(
                              (tab) => Container(
                                  child: tab['exercises'].length == 0
                                      ? Column(
                                          mainAxisAlignment:
                                              MainAxisAlignment.center,
                                          children: [
                                            Icon(
                                              Icons.sentiment_very_dissatisfied,
                                              size: size.width * 0.3,
                                              color: Colors.white,
                                            ),
                                            SizedBox(height: 10),
                                            Text(
                                              'This folder is empty. Add some workouts',
                                              style: TextStyle(
                                                  color: Colors.white),
                                              textAlign: TextAlign.center,
                                            ),
                                            SizedBox(height: size.height * .1),
                                          ],
                                        )
                                      : Container(
                                          height: size.height, // specify height
                                          child: ReorderableListView.builder(
                                            onReorder:
                                                (int oldIndex, int newIndex) {
                                              setState(() {
                                                if (newIndex > oldIndex) {
                                                  newIndex -= 1;
                                                }
                                                final item = tab['exercises']
                                                    .removeAt(oldIndex);
                                                tab['exercises']
                                                    .insert(newIndex, item);
                                              });
                                            },
                                            itemCount: tab['exercises'].length,
                                            itemBuilder: (BuildContext context,
                                                int index) {
                                              final exercise =
                                                  tab['exercises'][index];
                                              return Container(
                                                key: ValueKey(exercise),
                                                margin: EdgeInsets.all(5.0),
                                                decoration: BoxDecoration(
                                                  color: Colors.black,
                                                  borderRadius:
                                                      BorderRadius.circular(
                                                          10.0),
                                                ),
                                                child: Column(
                                                  children: [
                                                    Container(
                                                      height: size.height * .15,
                                                      decoration: BoxDecoration(
                                                        color: Color.fromARGB(
                                                            255, 45, 45, 45),
                                                        borderRadius:
                                                            BorderRadius
                                                                .circular(10.0),
                                                      ),
                                                      child: Stack(
                                                        children: [
                                                          Positioned(
                                                            top: 10,
                                                            left: 10,
                                                            child: Container(
                                                              width:
                                                                  size.width *
                                                                      .4,
                                                              child: ClipRRect(
                                                                borderRadius:
                                                                    BorderRadius
                                                                        .circular(
                                                                            10.0),
                                                                child: Image
                                                                    .network(
                                                                  'https://i.giphy.com/media/14kdiJUblbWBXy/giphy.gif', // Replace with your GIF URL
                                                                ),
                                                              ),
                                                            ),
                                                          ),
                                                          Positioned(
                                                            top: 10,
                                                            left: size.width *
                                                                    .4 +
                                                                20,
                                                            child: Column(
                                                              crossAxisAlignment:
                                                                  CrossAxisAlignment
                                                                      .start,
                                                              children: [
                                                                Text(
                                                                  exercise[
                                                                      'name'],
                                                                  style: TextStyle(
                                                                      color: Color.fromARGB(
                                                                          255,
                                                                          255,
                                                                          255,
                                                                          255),
                                                                      fontSize:
                                                                          20,
                                                                      fontWeight:
                                                                          FontWeight
                                                                              .bold),
                                                                ),
                                                                Text(
                                                                  'Muscles Worked: \n' +
                                                                      (exercise['musclesworked']
                                                                              as List)
                                                                          .join(
                                                                              ', '), // join the list into a string
                                                                  style: TextStyle(
                                                                      fontSize:
                                                                          16,
                                                                      color: Colors
                                                                          .white), // adjust style as needed
                                                                ),
                                                              ],
                                                            ),
                                                          ),
                                                        ],
                                                      ),
                                                    ),
                                                    SizedBox(height: 10),
                                                    Row(
                                                      children: [
                                                        Spacer(),
                                                        Text(
                                                          '3 Sets', // Replace with your sets, reps, and weight data
                                                          style: TextStyle(
                                                            color: Colors.white,
                                                          ),
                                                          textAlign:
                                                              TextAlign.center,
                                                        ),
                                                        Spacer(),
                                                        Text(
                                                          '4 reps', // Replace with your sets, reps, and weight data
                                                          style: TextStyle(
                                                            color: Colors.white,
                                                          ),
                                                          textAlign:
                                                              TextAlign.center,
                                                        ),
                                                        Spacer(),
                                                        Text(
                                                          '30 kg', // Replace with your sets, reps, and weight data
                                                          style: TextStyle(
                                                            color: Colors.white,
                                                          ),
                                                          textAlign:
                                                              TextAlign.center,
                                                        ),
                                                        Spacer(),
                                                      ],
                                                    ),
                                                    SizedBox(height: 10),
                                                  ],
                                                ),
                                              );
                                            },
                                          ))),
                            ),
                          ],
                        ),
                      ),
                    ),
                  ],
                ),
                Footer(tab: 'Workout'),
                //if (dropdownVisible)
                Positioned(
                    bottom: 60,
                    left: 0,
                    right: 0,
                    child: Column(children: [
                      Transform.translate(
                        offset: Offset(0, 0),
                        child: Center(
                          child: Container(
                            width: size.width * 0.8,                            
                            child: Row(
                              children: [
                                Spacer(),
                                Container(
                                  width: 30,
                                  height: 30,
                                  child: ElevatedButton(
                                    onPressed: () => {print('clicked')},
                                    style: ElevatedButton.styleFrom(
                                      padding: EdgeInsets.all(0),
                                      shape: RoundedRectangleBorder(
                                        borderRadius:
                                            BorderRadius.circular(1000.0),
                                      ),
                                      primary: Color.fromARGB(255, 49, 188, 10),
                                    ),
                                    child: Align(
                                      alignment: Alignment.center,
                                      child: Text(
                                        '+',
                                        style: TextStyle(
                                            fontSize: 25, color: Colors.white),
                                      ),
                                    ),
                                  ),
                                ),
                                
                              ],
                            ),
                          ),
                        ),
                      ),
                    ])),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
55ooxyrt

55ooxyrt1#

你需要确保你有所需的“组件”。请继续尝试这个:

  • 使用ReorderableListView.builder而不是使用列表中的map。
  • 使用每个ListTile或Container的key属性使它们可重新排序。每个项的键都应该是唯一的。

下面是一些例子:

ReorderableListView.builder(
  onReorder: (int oldIndex, int newIndex) {
    setState(() {
      if (newIndex > oldIndex) {
        newIndex -= 1;
      }
      final item = tab['exercises'].removeAt(oldIndex);
      tab['exercises'].insert(newIndex, item);
    });
  },
  itemCount: tab['exercises'].length,
  itemBuilder: (BuildContext context, int index) {
    final exercise = tab['exercises'][index];
    return Container(
      key: ValueKey(exercise),
      margin: EdgeInsets.all(5.0),
      child: CardListTile(
        title: Text(exercise['name']),
        subtitle: Text('Muscles Worked: ${exercise['musclesworked'].join(', ')}'),
      ),
    );
  },
),

itemBuilder回调用于构建列表项,其中Container Package ListTile,每个项都分配了一个ValueKey以进行重新排序。
除了使用builder,你还应该:
将ReorderableListView移动到CustomScrollView之外,并使用Expanded小部件 Package ReorderableListView,以允许它占用父容器中的剩余可用空间

body: Stack(
  children: [
    CustomScrollView(
      slivers: [
        SliverAppBar(
          // AppBar configs
        ),
        SliverToBoxAdapter(
          child: SizedBox(
            height: size.height - 170,
            child: PageView(
              // PageView configs
            ),
          ),
        ),
      ],
    ),
    Positioned(
      top: size.height,
      left: 0,
      right: 0,
      child: Transform.translate(
        offset: Offset(0, -120),
        child: Center(
          child: Container(
            // Control panel configs
          ),
        ),
      ),
    ),
    Footer(tab: 'Workout'),
  ],
),

希望现在能成功

相关问题