flutter

217 阅读5分钟
  1. main.dart初始化
main(){
  runApp(
      Text('123',textDirection: TextDirection.rtl,)
  );
}

以上text添加属性不会自动热重载,而是需要点击

, 因为只有在build中才能进行热重载
runApp是Flutter内部提供的一个函数,当我们启动一个Flutter应用程序时就是从调用这个函数开始的

  1. Widget
    flutter中万物皆可widget,及组件

  2. material设计风格

  3. scaffold 脚手架 搭建页面的基本结构

  4. flutter的劝退嵌套 可以封装自己的组件

  5. statelessWideget

class MyStatelessWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return <返回我们的Widget要渲染的Widget,比如一个Text Widget>;
  }
}

包含一个必须重写的方法build

build方法的解析

  • fluttter在拿到我们自己创建的statelesswidget时,聚会执行它的build方法
  • 我们需要在build方法中告诉flutter,我们的widget希望渲染什么元素,比如一个text widget
  • statelesswidget没办法主动去执行build方法,当我们使用的数据发生改变时,build方法会被重新执行

build方法什么情况下会被执行

  • 当我们的statelesswidget第一次被插入到widget树中时
  • 当我们的父widget发生改变时,子widget会被重新构建
  • 如果我们的widget以来inheriterwidget的一些数据,inheritedWidget数据发生改变时
  1. statefulwidget
    7.1 定义到widget中的数据都是不可变的(TODO),需要用final来修饰
    7.2 既然widget时不可变的,那么statefulwidget如何来存储可变的在状态呢
    fkutter将statefulwidget时必须创建两个类,一个类继承自statefulwidget,作为widget树的一部分,一个类继承子state,用于记录statefulwifget会变化的状态并且根据状态的变化,构建出新的widget
    7.3 生命周期

I/flutter (13258): HomeBody build
I/flutter (13258): 执行了MyCounterWidget的构造方法
I/flutter (13258): 执行了MyCounterWidget的createState方法
I/flutter (13258): 执行MyCounterState的构造方法
I/flutter (13258): 执行MyCounterState的init方法
I/flutter (13258): 执行MyCounterState的didChangeDependencies方法
I/flutter (13258): 执行执行MyCounterState的build方法
I/flutter (13258): HomeBody build
I/flutter (13258): 执行了MyCounterWidget的构造方法
I/flutter (13258): 执行MyCounterState的didUpdateWidget方法
I/flutter (13258): 执行执行MyCounterState的build方法

7.3.1 执行state类的构造方法(constructor)来创建对象
7.3.2 执行initstate,一般用于数据初始化或是发送网络请求 注意 这个方法是重写父类的方法,必须调用super,因为父类中会进行一些其他操作 7.3.3 执行didChangeDependencies,这个方法会在①调用initstate时 ② 从其他对象依赖一些数据发生改变时被调用
7.3.4 执行build
7.3.5 当前widget不再使用时,会调用disdpose进行销毁 7.3.6 手动调用setstate方法会根据最新的状态来重新调用build方法,构建对应的widgets
7.3.7 执行didUpdateWidget方法实在父widget触发重建时,系统会调用didUpdateWidget方法

  1. 图片widget
    加颜色滤镜
Container(
          child: Image.network(
            "http://img0.dili360.com/ga/M01/48/3C/wKgBy1kj49qAMVd7ADKmuZ9jug8377.tub.jpg",
            alignment: Alignment.topCenter,
            repeat: ImageRepeat.repeatY,
            color: Colors.blueGrey,
            colorBlendMode: BlendMode.colorDodge,
          ),
          width: 300,
          height: 300,
          color: Colors.yellow,
        ),
  1. boxdecoration
Container(
        width: 150,
        height: 150,
        child: Icon(Icons.pets, size: 32, color: Colors.white),
        decoration: BoxDecoration(
            color: Colors.amber, // 背景颜色
            border: Border.all(
                color: Colors.redAccent,
                width: 1,
                style: BorderStyle.solid
            ), // 这里也可以使用Border.all统一设置
            borderRadius: BorderRadius.circular(20), // 这里也可以使用.only分别设置
            boxShadow: [
              BoxShadow(
                  offset: Offset(3, 3),
                  color: Colors.deepPurpleAccent,
                  blurRadius: 5
              )
            ],
            gradient: LinearGradient(
                begin: Alignment.bottomLeft,
                end: Alignment.topRight,
                colors: [
                  Colors.blueGrey,
                  Colors.orangeAccent
                ]
            )
        ),
      )
  1. listview
    分割器
class MySeparatedDemo extends StatelessWidget {
  Divider blueColor = Divider(color: Colors.blue);
  Divider redColor = Divider(color: Colors.red);

  @override
  Widget build(BuildContext context) {
    return ListView.separated(
      itemBuilder: (BuildContext context, int index) {
        return ListTile(
          leading: Icon(Icons.people),
          title: Text("联系人${index+1}"),
          subtitle: Text("联系人电话${index+1}"),
        );
      },
      separatorBuilder: (BuildContext context, int index) {
        return index % 2 == 0 ? redColor : blueColor;
      },
      itemCount: 100
    );
  }
}
  1. slivers
    如果一个滑动的视图里需要一个标题视图,一个列表视图,一个网格视图,那么我们怎么能让他们做到统一的滑动效果呢,用customscrollview来统一管理多个滚动视图,在customscrollview中,每一个独立的可滚动的widget被称之为sliver
class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return showCustomScrollView();
  }

  Widget showCustomScrollView() {
    return new CustomScrollView(
      slivers: <Widget>[
        const SliverAppBar(
          expandedHeight: 250.0,
          flexibleSpace: FlexibleSpaceBar(
            title: Text('Coderwhy Demo'),
            background: Image(
              image: NetworkImage(
                "https://tva1.sinaimg.cn/large/006y8mN6gy1g72j6nk1d4j30u00k0n0j.jpg",
              ),
              fit: BoxFit.cover,
            ),
          ),
        ),
        new SliverGrid(
          gridDelegate: new SliverGridDelegateWithMaxCrossAxisExtent(
            maxCrossAxisExtent: 200.0,
            mainAxisSpacing: 10.0,
            crossAxisSpacing: 10.0,
            childAspectRatio: 4.0,
          ),
          delegate: new SliverChildBuilderDelegate(
            (BuildContext context, int index) {
              return new Container(
                alignment: Alignment.center,
                color: Colors.teal[100 * (index % 9)],
                child: new Text('grid item $index'),
              );
            },
            childCount: 10,
          ),
        ),
        SliverFixedExtentList(
          itemExtent: 50.0,
          delegate:
              SliverChildBuilderDelegate((BuildContext context, int index) {
            return new Container(
              alignment: Alignment.center,
              color: Colors.lightBlue[100 * (index % 9)],
              child: new Text('list item $index'),
            );
          }, childCount: 20),
        ),
        const SliverAppBar(
          backgroundColor: Colors.white,
          flexibleSpace: FlexibleSpaceBar(
            titlePadding: const EdgeInsets.all(0),
            title: Center(
              child: Text(
                '---end---',
                style: TextStyle(
                    color: Colors.black),
                textAlign: TextAlign.center,
              ),
            )
          ),
        ),
      ],
    );
  }
}
  1. 监听滚动事件
    12.1 滚动到指定位置时,出现返回顶部按钮
class MyHomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  ScrollController _controller;
  bool _isShowTop = false;

  @override
  void initState() {
    // 初始化ScrollController
    _controller = ScrollController();

    // 监听滚动
    _controller.addListener(() {
      var tempSsShowTop = _controller.offset >= 1000;
      if (tempSsShowTop != _isShowTop) {
        setState(() {
          _isShowTop = tempSsShowTop;
        });
      }
    });

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("ListView展示"),
      ),
      body: ListView.builder(
          itemCount: 100,
          itemExtent: 60,
          controller: _controller,
          itemBuilder: (BuildContext context, int index) {
            return ListTile(title: Text("item$index"));
          }
      ),
      floatingActionButton: !_isShowTop ? null : FloatingActionButton(
        child: Icon(Icons.arrow_upward),
        onPressed: () {
          _controller.animateTo(0, duration: Duration(milliseconds: 1000), curve: Curves.ease);
        },
      ),
    );
  }
}

12.2 监听什么时候开始滚动,什么事件结束滚动

class MyHomeNotificationDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => MyHomeNotificationDemoState();
}

class MyHomeNotificationDemoState extends State<MyHomeNotificationDemo> {
  int _progress = 0;

  @override
  Widget build(BuildContext context) {
    return NotificationListener(
      onNotification: (ScrollNotification notification) {
        // 1.判断监听事件的类型
        if (notification is ScrollStartNotification) {
          print("开始滚动.....");
        } else if (notification is ScrollUpdateNotification) {
          // 当前滚动的位置和总长度
          final currentPixel = notification.metrics.pixels;
          final totalPixel = notification.metrics.maxScrollExtent;
          double progress = currentPixel / totalPixel;
          setState(() {
            _progress = (progress * 100).toInt();
          });
          print("正在滚动:${notification.metrics.pixels} - ${notification.metrics.maxScrollExtent}");
        } else if (notification is ScrollEndNotification) {
          print("结束滚动....");
        }
        return false;
      },
      child: Stack(
        alignment: Alignment(.9, .9),
        children: <Widget>[
          ListView.builder(
              itemCount: 100,
              itemExtent: 60,
              itemBuilder: (BuildContext context, int index) {
                return ListTile(title: Text("item$index"));
              }
          ),
          CircleAvatar(
            radius: 30,
            child: Text("$_progress%"),
            backgroundColor: Colors.black54,
          )
        ],
      ),
    );
  }
}
  1. 封装一个打分widget
class HYStarRating extends StatefulWidget {
  final double rating;
  final double maxRating;
  final Widget unselectedImage;
  final Widget selectedImage;
  final int count;
  final double size;
  final Color unselectedColor;
  final Color selectedColor;

  HYStarRating({
    @required this.rating,
    this.maxRating = 10,
    this.size = 30,
    this.unselectedColor = const Color(0xffbbbbbb),
    this.selectedColor = const Color(0xffe0aa46),
    Widget unselectedImage,
    Widget selectedImage,
    this.count = 5,
  }): unselectedImage = unselectedImage ?? Icon(Icons.star, size: size, color: unselectedColor,),
        selectedImage = selectedImage ?? Icon(Icons.star, size: size, color: selectedColor);

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

class _HYStarRatingState extends State<HYStarRating> {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Stack(//重叠显示
        children: <Widget>[
          Row(children: getUnSelectImage(), mainAxisSize: MainAxisSize.min),
          Row(children: getSelectImage(), mainAxisSize: MainAxisSize.min),
        ],
      ),
    );
  }

  // 获取评星
  List<Widget> getUnSelectImage() {
    return List.generate(widget.count, (index) => widget.unselectedImage);
  }

  List<Widget> getSelectImage() {
    // 1.计算Star个数和剩余比例等 最高分数和星星个数比
    double oneValue = widget.maxRating / widget.count;
//   当前分数所占星星比例整数
    int entireCount = (widget.rating / oneValue).floor();
    double leftValue = widget.rating - entireCount * oneValue;
//    当前分数所占星星比例小数
    double leftRatio = leftValue / oneValue;


    // 2.获取start
    List<Widget> selectedImages = [];
    for (int i = 0; i < entireCount; i++) {
      selectedImages.add(widget.selectedImage);
    }

    // 3.计算 裁剪星星
    Widget leftStar = ClipRect(
      clipper: MyRectClipper(width: leftRatio * widget.size),
      child: widget.selectedImage,
    );
    selectedImages.add(leftStar);

    return selectedImages;
  }
}


class MyRectClipper extends CustomClipper<Rect>{
  final double width;

  MyRectClipper({
    this.width
  });

  @override
  Rect getClip(Size size) {
    return Rect.fromLTRB(0, 0, width, size.height);
  }

  @override
  bool shouldReclip(MyRectClipper oldClipper) {
    return width != oldClipper.width;
  }
}