Flutter 1.17 新 Material motion 規範的預構建動畫

老孟Flutter發表於2020-07-09

老孟導讀:在 Flutter 1.17 釋出大會上,Flutter 團隊還發布了新的 Animations 軟體包,該軟體包提供了實現新的 Material motion 規範的預構建動畫。

軟體包 pub 地址:pub.dev/packages/an…

Material motion 規範:material.io/design/moti…

引入外掛,版本號請到 pub 上檢視最新版本號:

animations: ^1.1.1複製程式碼

Container transform

容器轉換模式設計用於包含容器的UI元素之間的轉換。此模式在兩個UI元素之間建立可見連線。

案例:構建GridView,點選其中一項時跳轉到期詳情頁面:

GridView.builder(
  padding: EdgeInsets.all(8),
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: 2, crossAxisSpacing: 2, mainAxisSpacing: 4),
  itemBuilder: (context, index) {
    return OpenContainer(
      transitionDuration: _duration,
      closedBuilder: (BuildContext _, VoidCallback openContainer) {
        return Container(
          child: Image.asset(
            'assets/images/b.jpg',
            fit: BoxFit.fitWidth,
          ),
        );
      },
      openBuilder: (BuildContext context, VoidCallback _) {
        return _DetailPage();
      },
    );
  },
  itemCount: 50,
)複製程式碼

使用 OpenContainer 元件,closedBuilder 表示關閉狀態時到元件,在這裡表示 GridView Item,openBuilder 表示點選要跳轉的頁面,這裡表示詳情頁面。

詳情頁面程式碼如下:

class _DetailPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Container(
        width: double.infinity,
        height: double.infinity,
        child: Image.asset(
          'assets/images/b.jpg',
          fit: BoxFit.cover,
        ),
      ),
    );
  }
}複製程式碼

構建ListView

ListView.builder(
  itemBuilder: (context, index) {
    return OpenContainer(
      transitionDuration: _duration,
      closedBuilder: (BuildContext _, VoidCallback openContainer) {
        return Card(
          child: Container(
            height: 45,
            alignment: Alignment.center,
            child: Text('$index'),
          ),
        );
      },
      openBuilder: (BuildContext context, VoidCallback _) {
        return _DetailPage();
      },
    );
  },
  itemCount: 50,
)複製程式碼

也可以是一個按鈕,比如 floatingActionButton

Scaffold(
  body: _buildListView(),
  floatingActionButton: OpenContainer(
    openBuilder: (BuildContext context, VoidCallback _) {
      return _DetailPage();
    },
    transitionDuration: _duration,
    closedElevation: 6.0,
    closedShape: const RoundedRectangleBorder(
      borderRadius: BorderRadius.all(
        Radius.circular(50),
      ),
    ),
    closedColor: Theme.of(context).colorScheme.secondary,
    closedBuilder: (BuildContext context, VoidCallback openContainer) {
      return SizedBox(
        height: 50,
        width: 50,
        child: Center(
          child: Icon(
            Icons.add,
            color: Theme.of(context).colorScheme.onSecondary,
          ),
        ),
      );
    },
  ),
)複製程式碼

頂部輸入框

Scaffold(
  appBar: AppBar(
    title: OpenContainer(
      transitionDuration: _duration,
      closedBuilder: (BuildContext _, VoidCallback openContainer) {
        return Container(
          width: 300,
          height: 45,
          padding: EdgeInsets.only(left: 5),
          decoration: BoxDecoration(
              border: Border.all(color: Colors.grey.withOpacity(.5))),
          alignment: Alignment.centerLeft,
          child: Icon(Icons.search,color: Colors.black,),
        );
      },
      openBuilder: (BuildContext context, VoidCallback _) {
        return _DetailPage();
      },
    ),
  ),
)複製程式碼

Shared axis

共享軸模式用於具有空間或導航關係的UI元素之間的過渡。此模式在x,y或z軸上使用共享的變換來加強元素之間的關係。

底部導航案例:

@override
Widget build(BuildContext context) {
  Widget _child = _OnePage();
  switch (_currentIndex) {
    case 1:
      _child = _TwoPage();
      break;
  }
  return Scaffold(
    body: PageTransitionSwitcher(
      duration: const Duration(milliseconds: 1500),
      reverse: false,
      transitionBuilder: (
        Widget child,
        Animation<double> animation,
        Animation<double> secondaryAnimation,
      ) {
        return SharedAxisTransition(
          child: child,
          animation: animation,
          transitionType: SharedAxisTransitionType.horizontal,
          secondaryAnimation: secondaryAnimation,
        );
      },
      child: _child,
    ),
    bottomNavigationBar: BottomNavigationBar(
      onTap: (int index) {
        setState(() {
          _currentIndex = index;
        });
      },
      currentIndex: _currentIndex,
      items: <BottomNavigationBarItem>[
        BottomNavigationBarItem(title: Text('首頁'), icon: Icon(Icons.home)),
        BottomNavigationBarItem(
            title: Text('我的'), icon: Icon(Icons.perm_identity)),
      ],
    ),
  );
}複製程式碼

型別為 y 軸:

transitionType: SharedAxisTransitionType.vertical,複製程式碼

型別為 z 軸:

transitionType: SharedAxisTransitionType.scaled,複製程式碼

Fade through

淡入模式用於彼此之間沒有密切關係的UI元素之間的過渡。

下面案例來源於官方Demo:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: const Text('Fade through')),
    body: PageTransitionSwitcher(
      transitionBuilder: (
          Widget child,
          Animation<double> animation,
          Animation<double> secondaryAnimation,
          ) {
        return FadeThroughTransition(
          animation: animation,
          secondaryAnimation: secondaryAnimation,
          child: child,
        );
      },
      child: pageList[pageIndex],
    ),
    bottomNavigationBar: BottomNavigationBar(
      currentIndex: pageIndex,
      onTap: (int newValue) {
        setState(() {
          pageIndex = newValue;
        });
      },
      items: const <BottomNavigationBarItem>[
        BottomNavigationBarItem(
          icon: Icon(Icons.photo_library),
          title: Text('Albums'),
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.photo),
          title: Text('Photos'),
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.search),
          title: Text('Search'),
        ),
      ],
    ),
  );
}複製程式碼

效果適用於:

  1. 底部導航切換。
  2. 重新整理列表。
  3. 切換器。

Fade

淡入淡出模式用於在螢幕範圍內進入或退出的UI元素,例如在螢幕中央淡入淡出的對話方塊。

彈出對話方塊案例:

Scaffold(
  body: Center(
    child: RaisedButton(
      onPressed: () {
        showModal<void>(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              content: const Text('對話方塊'),
              actions: <Widget>[
                FlatButton(
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                  child: const Text('取消'),
                ),
                FlatButton(
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                  child: const Text('確定'),
                ),
              ],
            );
          },
        );
      },
      color: Theme.of(context).colorScheme.primary,
      textColor: Theme.of(context).colorScheme.onPrimary,
      child: const Text('彈出對話方塊'),
    ),
  ),
)複製程式碼

適用場景:

  1. dialog
  2. menu
  3. snackbar
  4. FloatingActionButton

交流

老孟Flutter部落格地址(330個控制元件用法):laomengit.com

歡迎加入Flutter交流群(微信:laomengit)、關注公眾號【老孟Flutter】:

相關文章