flutter

Flutter 常用UI及技巧

2020-11-24  本文已影响0人  gaookey
image.png

底部导航

final List<Widget> pages = [HomePage(), CategoryPage(), CartPage(), MePage()];

final List<BottomNavigationBarItem> items = [
  BottomNavigationBarItem(icon: Icon(Icons.home), label: "home"),
  BottomNavigationBarItem(icon: Icon(Icons.category), label: "category"),
  BottomNavigationBarItem(icon: Icon(Icons.shopping_cart), label: "cart"),
  BottomNavigationBarItem(icon: Icon(Icons.people), label: "people")
];

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

  @override
  State<GOBottomNavigationBar> createState() => _GOBottomNavigationBarState();
}

class _GOBottomNavigationBarState extends State<GOBottomNavigationBar> {
  int _currentIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // body: pages[_currentIndex],
      body: IndexedStack(
        index: _currentIndex,
        children: pages,
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        items: items,
        selectedItemColor: Colors.blue,
        unselectedItemColor: Colors.black,
        type: BottomNavigationBarType.fixed,
        onTap: (index) {
          setState(() {
            _currentIndex = index;
          });
        },
      ),
    );
  }
}
image.png

不规则底部导航

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

  @override
  State<GOBottomNavigationBar> createState() => _GOBottomNavigationBarState();
}

class _GOBottomNavigationBarState extends State<GOBottomNavigationBar> {
  int _currentIndex = 0;
  final List<Widget> _pages = [HomePage(), CategoryPage()];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.add),
          onPressed: () {
            Navigator.push(
                context,
                MaterialPageRoute(
                    builder: (context) {
                      return AddPage();
                    },
                    fullscreenDialog: true));
          },
        ),
        body: _pages[_currentIndex],
        floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
        bottomNavigationBar: BottomAppBar(
          color: Colors.blue,
          shape: CircularNotchedRectangle(),
          child: Row(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              IconButton(
                  onPressed: () {
                    setState(() {
                      _currentIndex = 0;
                    });
                  },
                  icon: Icon(Icons.home)),
              IconButton(
                  onPressed: () {
                    setState(() {
                      _currentIndex = 1;
                    });
                  },
                  icon: Icon(Icons.category)),
            ],
          ),
        ));
  }
}
image.png

路由跳转的动画效果

渐隐渐现
class CustomRouter extends PageRouteBuilder {
 final Widget _widget;

 CustomRouter(this._widget)
     : super(
           transitionDuration: Duration(seconds: 1),
           pageBuilder: (context, animation, secondaryAnimation) {
             return _widget;
           },
           transitionsBuilder:
               (context, animation, secondaryAnimation, child) {
             return FadeTransition(
                 child: child,
                 opacity: Tween(begin: 0.0, end: 2.0).animate(CurvedAnimation(
                     parent: animation, curve: Curves.linear)));
           });
}
Navigator.push(context, CustomRouter(SecondPage()));
渐隐渐现.gif
缩放
class CustomRouter extends PageRouteBuilder {
  final Widget _widget;

  CustomRouter(this._widget)
      : super(
            transitionDuration: Duration(seconds: 1),
            pageBuilder: (context, animation, secondaryAnimation) {
              return _widget;
            },
            transitionsBuilder:
                (context, animation, secondaryAnimation, child) {
              return ScaleTransition(
                child: child,
                scale: Tween(begin: 0.0, end: 1.0).animate(
                    CurvedAnimation(parent: animation, curve: Curves.linear)),
              );
            });
}
缩放.gif
旋转+缩放
class CustomRouter extends PageRouteBuilder {
  final Widget _widget;

  CustomRouter(this._widget)
      : super(
            transitionDuration: Duration(seconds: 1),
            pageBuilder: (context, animation, secondaryAnimation) {
              return _widget;
            },
            transitionsBuilder:
                (context, animation, secondaryAnimation, child) {
              return RotationTransition(
                  child: ScaleTransition(
                    child: child,
                    scale: Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
                        parent: animation, curve: Curves.linear)),
                  ),
                  turns: Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
                      parent: animation, curve: Curves.linear)));
            });
}
旋转缩放.gif
push
class CustomRouter extends PageRouteBuilder {
  final Widget _widget;
 
  CustomRouter(this._widget)
      : super(
            transitionDuration: Duration(seconds: 1),
            pageBuilder: (context, animation, secondaryAnimation) {
              return _widget;
            },
            transitionsBuilder:
                (context, animation, secondaryAnimation, child) {
              return SlideTransition(
                child: child,
                position: Tween(begin: Offset(-1.0, 0.0), end: Offset(0.0, 0.0))
                    .animate(CurvedAnimation(
                        parent: animation, curve: Curves.linear)),
              );
            });
}
push.gif

毛玻璃效果

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Stack(
      children: [
        ConstrainedBox(
          constraints: const BoxConstraints.expand(),
          child: Image.network("https://picsum.photos/500/500"),
        ),
        Center(
          child: ClipRect(
            child: BackdropFilter(
              filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
              child: Opacity(
                opacity: 0.5,
                child: Container(
                  width: 300,
                  height: 300,
                  decoration: BoxDecoration(
                    color: Colors.grey.shade200,
                  ),
                  child: Center(
                      child: Text(
                        "我是毛玻璃",
                        style: TextStyle(fontSize: 30.0, color: Colors.red),
                      )),
                ),
              ),
            ),
          ),
        )
      ],
    ),
  );
}
image.png

保持页面状态

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

  @override
  State<GOPage> createState() => _GOPageState();
}

class _GOPageState extends State<GOPage> with SingleTickerProviderStateMixin {
  late TabController _controller;

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("A"),
        bottom: TabBar(
          controller: _controller,
          tabs: [
            Tab(
              icon: Icon(Icons.home),
            ),
            Tab(
              icon: Icon(Icons.category),
            ),
            Tab(
              icon: Icon(Icons.people),
            ),
          ],
        ),
      ),
      body: TabBarView(
        controller: _controller,
        children: [
          HomePage(),
          HomePage(),
          HomePage(),
        ],
      ),
    );
  }
}
class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>
    with AutomaticKeepAliveClientMixin {
  int _counter = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              "$_counter",
              style: TextStyle(fontSize: 30, color: Colors.red),
            ),
            MaterialButton(
                color: Colors.lightBlue,
                child: Icon(Icons.add),
                onPressed: () {
                  setState(() {
                    _counter++;
                  });
                })
          ],
        ),
      ),
    );
  }

  @override
  bool get wantKeepAlive => true;
}
image.gif

搜索条

class GOSearchDelegate extends SearchDelegate<String> {
  @override
  List<Widget>? buildActions(BuildContext context) {
    return [
      IconButton(
          onPressed: () {
            query = "";
          },
          icon: Icon(Icons.clear))
    ];
  }

  @override
  Widget? buildLeading(BuildContext context) {
    return IconButton(
        onPressed: () {
          close(context, "1");
        },
        icon: AnimatedIcon(
          icon: AnimatedIcons.menu_arrow,
          progress: transitionAnimation,
        ));
  }

  @override
  Widget buildResults(BuildContext context) {
    return Container(
      width: 100,
      height: 100,
      child: Card(
        color: Colors.redAccent,
        child: Center(
          child: Text(query),
        ),
      ),
    );
  }

  @override
  Widget buildSuggestions(BuildContext context) {
    final List<String> _suggestionsList = query.isEmpty
        ? recList
        : searchList.where((element) => element.startsWith(query)).toList();

    return ListView.builder(
        itemCount: _suggestionsList.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: RichText(
              text: TextSpan(
                  style: TextStyle(
                      color: Colors.redAccent, fontWeight: FontWeight.bold),
                  text: _suggestionsList[index].substring(0, query.length),
                  children: [
                    TextSpan(
                      text: _suggestionsList[index].substring(query.length),
                      style: TextStyle(color: Colors.black38),
                    )
                  ]),
            ),
          );
        });
  }
}
showSearch(context: context, delegate: GOSearchDelegate());
image.gif

Wrap流式布局

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

  @override
  State<GOWarpPage> createState() => _GOWarpPageState();
}

class _GOWarpPageState extends State<GOWarpPage> {
  List<Widget> _list = [];

  @override
  void initState() {
    super.initState();
    _list.add(buildAddButton());
  }

  @override
  Widget build(BuildContext context) {
    final width = MediaQuery.of(context).size.width;
    final height = MediaQuery.of(context).size.height;

    return Scaffold(
      appBar: AppBar(
        title: Text("Flutter"),
      ),
      body: Center(
        child: Opacity(
          opacity: 0.8,
          child: Container(
            width: width,
            height: height * 0.5,
            color: Colors.black26,
            child: Wrap(
              spacing: 20,
              children: _list,
            ),
          ),
        ),
      ),
    );
  }

  Widget buildPhoto() {
    return Padding(
      padding: EdgeInsets.all(8),
      child: Container(
        width: 80,
        height: 80,
        color: Colors.teal,
        child: Center(
          child:
              Image.network("https://picsum.photos/300/300", fit: BoxFit.fill),
        ),
      ),
    );
  }

  Widget buildAddButton() {
    return GestureDetector(
      onTap: () {
        if (_list.length < 9) {
          setState(() {
            _list.insert(_list.length - 1, buildPhoto());
          });
        }
      },
      child: Padding(
        padding: EdgeInsets.all(8),
        child: Container(
          width: 80,
          height: 80,
          color: Colors.grey,
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}
image.gif

ExpansionTile

class ExpansionTilePage extends StatelessWidget {
  const ExpansionTilePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Flutter"),
      ),
      body: Center(
        child: ExpansionTile(
          title: Text("Expansion Tile"),
          leading: Icon(Icons.ac_unit),
          backgroundColor: Colors.amberAccent,
          children: [
            ListTile(
              title: Text("title"),
              subtitle: Text("subtitle"),
            )
          ],
          initiallyExpanded: true, // 默认打开
        ),
      ),
    );
  }
}
image.gif

ExpansionPanelList

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

  @override
  State<ExpansionPanelListPage> createState() => _ExpansionPanelListPageState();
}

class _ExpansionPanelListPageState extends State<ExpansionPanelListPage> {
  List<int> _list = [];
  List<ExpansionStateModel> _models = [];

  _ExpansionPanelListPageState() {
    for (int i = 0; i < 20; i++) {
      _list.add(i);
      _models.add(ExpansionStateModel(i, false));
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Flutter"),
      ),
      body: SingleChildScrollView(
        child: ExpansionPanelList(
          expansionCallback: (panelIndex, isExpanded) {
            _setCurrentIndex(panelIndex, isExpanded);
          },
          children: _list.map((e) {
            return ExpansionPanel(
                headerBuilder: (context, isExpanded) {
                  return ListTile(
                    title: Text("No. $e"),
                  );
                },
                body: ListTile(
                  title: Text("expansion no. $e"),
                ),
                isExpanded: _models[e].isOpen);
          }).toList(),
        ),
      ),
    );
  }

  _setCurrentIndex(int index, bool isExpanded) {
    setState(() {
      _models.forEach((element) {
        if (element.index == index) {
          element.isOpen = !isExpanded;
        }
      });
    });
  }
}

class ExpansionStateModel {
  var isOpen;
  var index;

  ExpansionStateModel(this.index, this.isOpen);
}
image.gif

路径裁切和二次贝塞尔曲线

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
          child: ClipPath(
        clipper: BottomClipper(),
        child: Container(
          color: Colors.redAccent,
          height: 150,
        ),
      )),
    );
  }
}
class BottomClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    var path = Path();
    path.lineTo(0, 0);
    path.lineTo(0, size.height - 50);

    var beginPoint = Offset(size.width * 0.5, size.height);
    var endPoint = Offset(size.width, size.height - 50);
    path.quadraticBezierTo(beginPoint.dx, beginPoint.dy,
        endPoint.dx, endPoint.dy);

    path.lineTo(size.width, size.height - 50);
    path.lineTo(size.width, 0);
    return path;
  }

  @override
  bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
    return false;
  }
}
image.png

class BottomClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    var path = Path();
    path.lineTo(0, 0);
    path.lineTo(0, size.height - 50);

    var beginPoint = Offset(size.width * 0.25, size.height);
    var endPoint = Offset(size.width * 0.5, size.height - 50);
    path.quadraticBezierTo(
        beginPoint.dx, beginPoint.dy, endPoint.dx, endPoint.dy);

    var beginPoint2 = Offset(size.width * 0.75, size.height - 100);
    var endPoint2 = Offset(size.width, size.height - 50);
    path.quadraticBezierTo(
        beginPoint2.dx, beginPoint2.dy, endPoint2.dx, endPoint2.dy);

    path.lineTo(size.width, size.height - 50);
    path.lineTo(size.width, 0);
    return path;
  }

  @override
  bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
    return false;
  }
}
image.png

APP闪屏动画

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

  @override
  State<GOAnimationPage> createState() => _GOAnimationPageState();
}

class _GOAnimationPageState extends State<GOAnimationPage> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
        vsync: this, duration: Duration(milliseconds: 3000));
    _animation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
    _animation.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        Navigator.pushAndRemoveUntil(
            context,
            MaterialPageRoute(builder: (context) => HomePage()),
            (route) => route == null);
      }
    });
    _controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return FadeTransition(
      opacity: _animation,
      child: Image.network(
        "https://picsum.photos/300/300",
        fit: BoxFit.fill,
        scale: 2.0,
      ),
    );
  }

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

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(child: Text("我是首页")),
    );
  }
}
image.gif

右滑返回 cupertino

import 'package:flutter/cupertino.dart';

class RightBackPage extends StatelessWidget {
  const RightBackPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
        child: Center(
      child: Container(
        height: 100,
        width: 100,
        color: CupertinoColors.activeBlue,
        child: CupertinoButton(
          child: Icon(CupertinoIcons.add),
          onPressed: () {
            Navigator.push(context, CupertinoPageRoute(builder: (context) {
              return RightBackPage();
            }));
          },
        ),
      ),
    ));
  }
}
image.gif

Tooltip

当widget长按时显示一个提示标签

class TooltipPage extends StatelessWidget {
  const TooltipPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Tooltip(
          child: Container(
            width: 100,
            height: 100,
            color: Colors.amber,
          ),
          message: "长按了 Container !!!",
          textStyle: TextStyle(fontSize: 22, color: Colors.redAccent),
          showDuration: Duration(seconds: 2),
        ),
      ),
    );
  }
}
image.gif

Draggable 拖拽控件

class GODraggable extends StatefulWidget {
  Offset? offset;
  Color? widgetColor;

  GODraggable({this.offset, this.widgetColor});

  @override
  State<GODraggable> createState() => _GODraggableState();
}

class _GODraggableState extends State<GODraggable> {
  @override
  Widget build(BuildContext context) {
    return Positioned(
        left: widget.offset?.dx,
        top: widget.offset?.dy,
        child: Draggable(
          data: widget.widgetColor,
          child: Container(
            width: 100,
            height: 100,
            color: widget.widgetColor,
          ),
          feedback: Container(
            width: 120,
            height: 120,
            color: widget.widgetColor?.withOpacity(0.5),
          ),
          onDraggableCanceled: (velocity, offset) {
            setState(() {
              widget.offset = offset;
            });
          },
        ));
  }
}
class GODraggablePage extends StatefulWidget {
  const GODraggablePage({Key? key}) : super(key: key);

  @override
  State<GODraggablePage> createState() => _GODraggablePageState();
}

class _GODraggablePageState extends State<GODraggablePage> {
  Color _color = Colors.black12;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          GODraggable(
            offset: Offset(80, 80),
            widgetColor: Colors.amber,
          ),
          GODraggable(
            offset: Offset(200, 80),
            widgetColor: Colors.redAccent,
          ),
          Center(
            child: DragTarget(
              onAccept: (Color color) {
                _color = color;
              },
              builder: (context, candidateData, rejectedData) {
                return Container(
                  width: 300,
                  height: 300,
                  color: _color,
                );
              },
            ),
          )
        ],
      ),
    );
  }
}
image.gif

本篇文章内容学习自:20个Flutter实例视频教程 让你轻松上手工作

上一篇 下一篇

猜你喜欢

热点阅读