欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

flutter一文通4

程序员文章站 2022-05-30 17:37:29
...

flutter图标库:http://fluttericon.com/

一. ClipOval椭圆组件

使被包裹的子组件椭圆化

ClipOval(
            child: Image.network("https://timgsa.baidu.com/timg68"),
          ),
flutter一文通4

二. ClipRRect 圆角矩形

使被包裹的子组件圆角矩形化

ClipRRect(
            borderRadius: BorderRadius.circular(16),
            child: Image.network("https://timgsa.baidu.com/timg68"),
          ),
flutter一文通4
image.png

三. form表单

form也是一个组件,与它配套的是可以提交内容的组件,比如:

  • TextFormField

提交过程很繁琐:

  1. 提前声明一个GlobalKey,用来绑定form GlobalKey<FormState> formGlobalKey = GlobalKey();
  2. 给form的key属性绑定这个GlobalKey
  3. 在配套组件中注册提交事件 onSaved: (value){}
  4. 在提交按钮中用GlobalKey找到form,并调用它的formGlobalKey.currentState.save();

这样,在按钮点击时,表单会触发与它配套的组件的onSaved: (value){}方法,并将他们的值传给这个方法

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: Scaffold(
        appBar: AppBar(
          title: Text("data"),
        ),
        body: Login(),
      ),
    );
  }
}

class Login extends StatefulWidget {
  @override
  _LoginState createState() => _LoginState();
}

class _LoginState extends State<Login> {
  String username;  
  String password;

  GlobalKey<FormState> formGlobalKey = GlobalKey();
  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(10),
      child: Form(
          key: formGlobalKey,
          child: Column(
            children: <Widget>[
              TextFormField(
                decoration: InputDecoration(
                    icon: Icon(Icons.people), labelText: 'USERs'),
                onSaved: (value) {
                  setState(() {
                    print(value);
                    this.username = value;
                  });
                },
              ),
              TextFormField(
                decoration: InputDecoration(
                    icon: Icon(Icons.lock), labelText: 'password'),
                obscureText: true,
                onSaved: (value) {
                  setState(() {
                    print(value);
                    this.password = value;
                  });
                },
              ),
              RaisedButton(
                  child: Container(
                    width: double.infinity,
                    alignment: Alignment.center,
                    child: Text(
                      "login",
                      style: TextStyle(color: Colors.white),
                    ),
                  ),
                  color: Colors.green,
                  onPressed: () {
                    setState(() {
                      print("OK");
                      formGlobalKey.currentState.save();
                    });
                  })
            ],
          )),
    );
  }
}

四. 表单验证器

我们想验证表单里的内容输入的合法性,这需要:

  1. 在form配套子组件中添加属性validator
    validator属性里面如何设置?
    validator属性里要传入一个函数,这个函数默认接收value参数,可以对value做一些判断,如果合规就return null,如果不合规就return一个字符串错误提示
validator: (value) {
  if (value.length == 0) {
    return "密码不能为空";
  }
  return null;
},
  1. 在提交按钮中触发formGlobalKey.currentState.validate();

五. 表单自动验证

很简单,在form配套子组件中添加属性autovalidate: true,
同时,可以省略在提交按钮中触发formGlobalKey.currentState.validate();这一步了

六. 主题设置

一个APP我们往往要求色调统一等全局性设置,这些设置都被放在了根组件MaterialApp的theme属性里.
主题数据里的设置非常非常多,而且全都是跟颜色 动画等APP风格有关的设置
例,设置方法:

class MyApp extends StatelessWidget {
  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: _title,
      home: MyStatefulWidget(),
      theme: ThemeData(primaryColor: Colors.green),
    );
  }
}

七. 底部导航栏

  • 底部导航栏BottomNavigationBar里面要放至少两个BottomNavigationBarItem,而且每个BottomNavigationBarItem必须有title
  • BottomNavigationBar有一个关键属性currentIndex,此属性传入数字,传入几第几个标签就会标记为按下
  • BottomNavigationBar有一个ontap事件,事件会在点击BottomNavigationBarItem时触发,事件默认传入一个参数value, 其指代的是被点击的BottomNavigationBarItem的index
  • 底栏切换时,脚手架的home也随之切换,实现的方式是:把所有要显示的内容放在列表中,用elementAt(_selectedIndex)来切换
import 'package:flutter/material.dart';

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

/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: _title,
      home: MyStatefulWidget(),
      theme: ThemeData(primaryColor: Colors.green),
    );
  }
}

class MyStatefulWidget extends StatefulWidget {
  MyStatefulWidget({Key key}) : super(key: key);

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

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  int _selectedIndex = 0;
  static const TextStyle optionStyle =
      TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
  static const List<Widget> _widgetOptions = <Widget>[
    Text(
      'Index 0: Home',
      style: optionStyle,
    ),
    Text(
      'Index 1: Business',
      style: optionStyle,
    ),
    Text(
      'Index 2: School',
      style: optionStyle,
    ),
  ];

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('BottomNavigationBar Sample'),
      ),
      body: Center(
        child: _widgetOptions.elementAt(_selectedIndex),
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            title: Text('首页'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.business),
            title: Text('分类'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.school),
            title: Text('我的'),
          ),
        ],
        currentIndex: _selectedIndex,
        onTap: _onItemTapped,
      ),
    );
  }
}
flutter一文通4

八. 封装一个自己的组件

建立一个文件 如:components/my_bottom_bar.dart

import 'package:flutter/material.dart';
 
class MyBottomBar extends BottomNavigationBarItem{
  MyBottomBar(String name,Icon icon,Icon activeIcon):super(
    icon: icon,
    activeIcon:activeIcon,
    title:Text(name)
  );
}

引用时:

import './components/my_bottom_bar.dart';

九. 导航条配合神器 IndexedStack组件

IndexedStack会根据index选择自己要显示的子组件

flutter一文通4
import 'package:flutter/material.dart';
class MyBottomNavBar extends StatefulWidget {
  @override
  _MyBottomNavBarState createState() => _MyBottomNavBarState();
}

class _MyBottomNavBarState extends State<MyBottomNavBar> {
  int _currentIndex=0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("底部导航栏"),),
      body: IndexedStack(
        index: _currentIndex,
        children: <Widget>[
          Text("首页"),
          Text("商店页"),
        ],
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.home),title: Text("home")),
          BottomNavigationBarItem(icon: Icon(Icons.shop),title: Text("shop")),
        ],
        onTap: (index){
          setState(() {
            _currentIndex=index;
          });
        },
        ),
    );
  }
}

十. 状态组件中状态管理器如何拿到组件的变量

使用widget.xxx

import 'package:flutter/material.dart';

class EachView extends StatefulWidget {
  final String _title;
  EachView(this._title);
  @override
  _EachViewState createState() => _EachViewState();
}

class _EachViewState extends State<EachView> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget._title)),
      body: Center(
        child: Text(widget._title),
      ),
    );
  }
}

十一. 搜索框

先看效果:

flutter一文通4
import 'package:flutter/material.dart';
import './asset.dart'; //数据本应该是数据库提供的 这里我们写了个假数据文件假扮.

class SearchBarDemo extends StatefulWidget {
  @override
  _SearchBarDemoState createState() => _SearchBarDemoState();
}

class _SearchBarDemoState extends State<SearchBarDemo> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("搜索DEMO"),
        actions: <Widget>[
          IconButton(
              icon: Icon(Icons.search),
              onPressed: () {
                //点击搜索按钮后会调用原生showSearch方法显示SearchBarDelegate搜索条
                showSearch(context: context, delegate: SearchBarDelegate());
              })
        ],
      ),
    );
  }
}

class SearchBarDelegate extends SearchDelegate<String> {
  @override
  List<Widget> buildActions(BuildContext context) {
    //重写bulidActions 构造搜索动作区域  就是后面的X  这个重构基本不会变
    return [IconButton(icon: Icon(Icons.clear), onPressed: () => query = "")];
  }

  @override
  Widget buildLeading(BuildContext context) {
    //重写buildLeading 构造搜索头部区域  就是前面的⬅  这个重构基本不会变
    return IconButton(
        icon: AnimatedIcon(
            icon: AnimatedIcons.menu_arrow, progress: transitionAnimation),
        onPressed: () => close(context, null));
  }

  @override
  Widget buildResults(BuildContext context) {
    //重写buildResults 构造搜索结果区域, 这里写的是搜索按钮按下后的逻辑
    return Container(
      width: 400.0,
      height: 200.0,
      child: Card(
        //点击搜索后可以显示列表清单\特等结果等, 这里是假装搜索到一个卡片.
        color: Colors.indigo,
        child: Text(
          '这里是假装的搜索$query 得到的结果!!最好做成一个列表,通过点击跳转到想去的页面',
          style: TextStyle(color: Colors.white, fontSize: 20),
          textAlign: TextAlign.center,
        ),
      ),
    );
  }

  @override
  Widget buildSuggestions(BuildContext context) {
    //重写buildSuggestions 构造搜索提示区域
    final suggestionList = query.isEmpty //搜索提示列表数据
        ? recentSuggest //最近搜索, 数据应来源于服务器
        : searchList
            .where((input) => input.startsWith(query))
            .toList(); //数据应来源于服务器
    //重写buildSuggestions  构造搜索提示
    return ListView.builder(
        //生成提示列表
        itemBuilder: (context, index) => ListTile(
              //返回列表瓦片
              title: RichText(
                text: TextSpan(
                    //严格匹配的加粗部分
                    text: suggestionList[index].substring(0, query.length),
                    style: TextStyle(
                        color: Colors.black, fontWeight: FontWeight.bold),
                    children: [
                      TextSpan(
                          //未严格匹配到的部分
                          text: suggestionList[index].substring(query.length),
                          style: TextStyle(color: Colors.grey))
                    ]),
              ),
              onTap: () {
                print(query);
                Scaffold.of(context).removeCurrentSnackBar();
                Scaffold.of(context).showSnackBar(SnackBar(
                  content: Text("一个建议选项被点了,可能要跳转到对应的页面了"),
                ));
              },
            ),
        itemCount: suggestionList.length);
  }
}

十二. 闪屏页面 //启动加载页

flutter一文通4
import 'package:flutter/material.dart';
import './home_page.dart';

class SplashScreen extends StatefulWidget {
  @override
  _SplashScreenState createState() => _SplashScreenState();
}

class _SplashScreenState extends State<SplashScreen>
    with SingleTickerProviderStateMixin { //为了动画效果混合这个类SingleTickerProviderStateMixin
  AnimationController _controller;  //动画控制器
  Animation _animation;   //动画过程

  @override
  void initState() {
    //重写初始化方法
    super.initState();
    _controller = AnimationController(   //初始化动画控制器
        vsync: this, duration: Duration(milliseconds: 3000));
    _animation = Tween(begin: 0.0, end: 1.0).animate(_controller); //初始化动画过程
 
    _animation.addStatusListener((status) {       //监听动画状态变化
      if (status == AnimationStatus.completed) {  //当动画状态变为AnimationStatus.completed时,发生跳转
        Navigator.of(context).pushAndRemoveUntil(
            MaterialPageRoute(builder: (context) => MyHomePage()),  //跳转到MyHomePage页面
            (router) => router == null);   //固定写法
      }
    });
    _controller.forward(); //播放动画
  }

  @override
  void dispose() {
    //重写销毁方法
    _controller.dispose(); //销毁动画控制器
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return FadeTransition(  //返回一个淡入动画
      opacity: _animation,  //该动画的透明度关联至上面创立的动画过程_animation身上
      child: Image.network(
        "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1587370522470&di=e0ba28a377b1d9d18a5589579af3a35a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201611%2F27%2F20161127000843_zLhCx.jpeg",
        scale: 2.0,
        fit: BoxFit.cover,
      ),
    );
  }
}

十三. 右滑返回

实现这个效果,我们引用了另一套UI框架cupertino.这套风格接近IOS风格.

flutter一文通4
import 'package:flutter/cupertino.dart'; //引用的是另一套UI风格:cupertino

class RightBackDemo extends StatelessWidget {
  //第二页,里面有个卡布奇诺按钮, 按下返回第一页
  @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: (BuildContext context) {
              return FirstPage();
            }));
          }),
    )));
  }
}

class FirstPage extends StatelessWidget {
  //第一页,只是一个普通的页面
  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      child: Center(
        child: Container(
          child: CupertinoButton(
              child: Text('next page'),
              onPressed: () {
                Navigator.push(context,
                    CupertinoPageRoute(builder: (BuildContext context) {
                  return RightBackDemo();
                }));
              }),
        ),
      ),
    );
  }
}

十四. tooltip 轻提示

tooltip常常用在图片 按钮上,做小型提示.

flutter一文通4
import 'package:flutter/material.dart';

class TooltipDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("tooltip")),
      body: Center(
        child: Tooltip(
          message: '不要乱点',
          preferBelow:true,
          child: Image.network(
            "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1587630407426&di=dc83b562a13e8f2f870dc19b5dc4fe05&imgtype=0&src=http%3A%2F%2Fattachments.gfan.com%2Fforum%2F201803%2F23%2F1849303zhiwhlvviwplm5l.jpg",
          ),
        ),
      ),
    );
  }
}