1

我是新来的颤振

我正在学习并制作了这个布局。

布局结构:

    App
    │
    └── MaterialApp
        │
        └── Scaffold
            │
            └── AppBar
            │
            └── TabBar
            │   │
            │   └── Tab
            │
            └── Container
                │
                └── Expanded
                │   │
                │   └── TabBarView
                │       │
                │       └── Container
                │           │
                │           └── GridView
                │
                └── Container
                    │
                    └── Column
                        │
                        └── Row
                            │
                            └── CircularButton(A)
                            │
                            └── CircularButton(B)

我想GridView在布局首次生成时以及用户选择CircularButton(正在重新生成GridView)时设置动画。如何实现这个动画?

在此处输入图像描述

我到目前为止的代码:

import 'package:flutter/material.dart';

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

class App extends StatefulWidget {
  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<App> with SingleTickerProviderStateMixin {

  List<String> data = DataClass.dataOne;
  static String selectedData = 'A';

  final List<Tab> myTabs = <Tab>[
    Tab(
      text: 'SELECTED DATA $selectedData',
    ),
  ];

  List<bool> currentSelectedList = DataClass.dataTwo;

  List<bool> changeSelection() {
    return currentSelectedList;
  }

  TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(vsync: this, length: myTabs.length);
  }

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'GridView Test',
      theme: ThemeData(
        primaryColor: Color(0xFFD0021B),
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text(
            'GridView Test',
            style: TextStyle(
              fontFamily: 'Roboto',
            ),
          ),
          bottom: TabBar(
            controller: _tabController,
            tabs: <Tab>[
              Tab(
                text: 'SELECTED DATA $selectedData',
              ),
            ],
            indicatorColor: Colors.white,
          ),
        ),
        body: Container(
          color: Color(0xFFD0021B),
          child: Column(
            children: <Widget>[
              Expanded(
                child: TabBarView(
                  controller: _tabController,
                  children: [
                    Container(
                      child: Grid(changeSelection()),
                    ),
                  ],
                ),
              ),
              Container(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: <Widget>[
                    Row(
                      mainAxisSize: MainAxisSize.max,
                      mainAxisAlignment: MainAxisAlignment.spaceAround,
                      children: <Widget>[
                        circleButton(DataClass.dataOne[0]),
                        circleButton(DataClass.dataOne[1]),
                      ],
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget circleButton(String type) {

    return Padding(
      padding: const EdgeInsets.only(top: 8.0, bottom: 10.0),
      child: FloatingActionButton(
        onPressed: () {
          setState(() {
            switch (type) {
              case 'A':
                selectedData = DataClass.dataOne[0];
                currentSelectedList = DataClass.dataTwo;
                break;
              case 'B':
                selectedData = DataClass.dataOne[1];
                currentSelectedList = DataClass.dataThree;
                break;
            }
          });
        },
        child: Text(
          type,
          style: TextStyle(
            fontFamily: 'Roboto',
            fontSize: 25.0,
            color: Color(0xFFD0021B),
          ),
        ),
        backgroundColor: Colors.white,
      ),
    );
  }

}

class DataClass {
  static List<String> dataOne = ['A', 'B'];

  static List<bool> dataTwo = [true, false, true, false, true, false, true, false];
  static List<bool> dataThree = [true, true, true, true, true, true, true, true];
}

class Grid extends StatelessWidget {
  Grid(this.available);
  final List<bool> available;
  @override
  Widget build(BuildContext context) {
    return GridView.count(
      children: <Widget>[
        Tile(DataClass.dataOne[0], available[0]),
        Tile(DataClass.dataOne[1], available[1]),
        Tile(DataClass.dataOne[0], available[2]),
        Tile(DataClass.dataOne[1], available[3]),
        Tile(' ', true),
        Tile(DataClass.dataOne[0], available[4]),
        Tile(DataClass.dataOne[1], available[5]),
        Tile(DataClass.dataOne[0], available[6]),
        Tile(DataClass.dataOne[1], available[7]),
      ],
      crossAxisCount: 3,
      crossAxisSpacing: 20.0,
      primary: false,
      padding: const EdgeInsets.all(20.0),
      mainAxisSpacing: 1.0,
    );
  }
}

class Tile extends StatelessWidget {
  Tile(this.data, this.enabled);
  final data;
  final enabled;
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(10.0),
      child: FloatingActionButton(
        shape: BeveledRectangleBorder(
          borderRadius: BorderRadius.circular(20.0),
        ),
        onPressed: () {},
        child: data.contains(' ')
            ? Icon(
          Icons.all_out,
          color: Color(0xFF820111),
          size: 50.0,
        )
            : Text(
          data,
          style: TextStyle(
            fontFamily: 'Roboto',
            fontSize: 25.0,
            color: enabled ? Theme.of(context).primaryColor : Color(0xFFFFFFFF),
          ),
        ),
        backgroundColor: enabled ? Color(0xFFFFFFFF) : Color(0xFF999999),
      ),
    );
  }
}
4

1 回答 1

0

您可以通过使用Stack而不是 GridView 来实现类似的行为 - 这允许我们将小部件相互重叠。要在屏幕上移动 Widget,我们可以使用AnimatedPositioned

这是一个完整的示例。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool _expand = false;
  double? top;
  double? left;
  List<double> _listTop = [];
  List<double> _listLeft = [];

  // initialize screen position for expanded and retracted widgets
  _initPosition(double screenWidth) {
    for (var x = 0; x < 9; x++) {
      if (x < 3) _listTop.add(0);
      if (x > 2 && x < 6) _listTop.add(screenWidth / 3);
      if (x > 5) _listTop.add((screenWidth / 3) * 2);

      if (x % 3 == 0) _listLeft.add(0);
      if (x % 3 == 1) _listLeft.add(screenWidth / 3);
      if (x % 3 == 2) _listLeft.add((screenWidth / 3) * 2);
    }
  }

  @override
  Widget build(BuildContext context) {
    // we can only fetch the screen size on build
    _initPosition(MediaQuery.of(context).size.width);
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Expanded(
              flex: 3,
              child: Container(
                color: Colors.greenAccent,
                child: Stack(
                  alignment: AlignmentDirectional.center,
                  children: List.generate(9, (index) {
                    return AnimatedPositioned(
                      duration: Duration(milliseconds: 800),
                      top: _expand
                          ? _listTop[index]
                          : ((top != null)
                              ? top
                              : MediaQuery.of(context).size.width / 3),
                      left: _expand
                          ? _listLeft[index]
                          : ((left != null)
                              ? left
                              : MediaQuery.of(context).size.width / 3),
                      child: Card(
                        elevation: 0.5,
                        child: Container(
                          height: MediaQuery.of(context).size.width / 3.5,
                          width: MediaQuery.of(context).size.width / 3.5,
                          color: Colors.lightBlue,
                          child: Center(
                            child: Text(
                              'Item $index',
                              style: Theme.of(context).textTheme.headline5,
                            ),
                          ),
                        ),
                      ),
                    );
                  }),
                ),
              ),
            ),
            Expanded(
              flex: 1,
              child: Row(
                children: [
                  Expanded(
                    child: ElevatedButton(
                      onPressed: () {
                        setState(() {
                          _expand = true;
                        });
                      },
                      child: Padding(
                        padding: const EdgeInsets.all(16.0),
                        child: Text('+'),
                      ),
                      style: ElevatedButton.styleFrom(
                        shape: CircleBorder(),
                      ),
                    ),
                  ),
                  Expanded(
                    child: ElevatedButton(
                      onPressed: () {
                        setState(() {
                          _expand = false;
                        });
                      },
                      child: Padding(
                        padding: const EdgeInsets.all(16.0),
                        child: Text('-'),
                      ),
                      style: ElevatedButton.styleFrom(
                        shape: CircleBorder(),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ), 
    );
  }
}

演示

于 2021-07-22T14:40:04.363 回答