Continuing the update to the Flutter blog series, Google released version 1.0 of Flutter yesterday 14th December (Stable). Unlike many other digs Google has made, it has been only ten months since the release of The Release number of Flutter to the first Stable version. Google’s positioning of Flutter is by no means an experimental framework. Although some people say that the release of version numbers is free, it at least shows the official attitude, like Facebook ReactNative, which has been continuously updated for three years. The latest version is 0.57, but there is still no release 1.0. Add to that the fact that many developers have abandoned maintenance using ReactNative and related open source libraries. Google announced yesterday that a framework for desktop and Web application development will be released soon. This will make Flutter a truly cross-app/desktop /Web UI framework. This article describes how to use the PageView component to achieve the parallax effect of the image preview, the effect picture is as follows:

Create a project

Open AndroidStudio, create a new Flutter project, adjust the main.dar file, clean up the useless code for the demo, and get the following code:


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')); }}class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

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


class _MyHomePageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center( ), ); }}Copy the code

There’s nothing to say in the code above. Go ahead.

Using a PageView

Add a PageView component to ‘_MyHomePageState’ and display several images by modifying the build method:

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Container(
          child: PageView(
            children: <Widget>[
              Image.network('https://ws1.sinaimg.cn/large/0065oQSqgy1fwgzx8n1syj30sg15h7ew.jpg'),
              Image.network('https://ws1.sinaimg.cn/large/0065oQSqly1fw8wzdua6rj30sg0yc7gp.jpg'),
              Image.network('https://ws1.sinaimg.cn/large/0065oQSqly1fw0vdlg6xcj30j60mzdk7.jpg'),
              Image.network('https://ws1.sinaimg.cn/large/0065oQSqly1fuo54a6p0uj30sg0zdqnf.jpg'),
            ],
          ),
          width: 200,
          height: 200,
        ),
      ),
    );
  }
Copy the code

The basic usage of PageView above, through the above code, we can get a picture preview component can turn the page left and right. Next create a PageController to listen for PageView’s PageController callback, create a PageController object in the _MyHomePageState class, add a listener in initState, Assign the Created PageController object to the PageView component in the Build method. The following code

class _MyHomePageState extends State<MyHomePage> {
  var imgUrlList = [
    'https://ws1.sinaimg.cn/large/0065oQSqgy1fwgzx8n1syj30sg15h7ew.jpg'.'https://ws1.sinaimg.cn/large/0065oQSqly1fw8wzdua6rj30sg0yc7gp.jpg'.'https://ws1.sinaimg.cn/large/0065oQSqly1fw0vdlg6xcj30j60mzdk7.jpg'.'https://ws1.sinaimg.cn/large/0065oQSqly1fuo54a6p0uj30sg0zdqnf.jpg'
  ];
  
  PageController controller = new PageController();
  
  @override
  void initState() {
    super.initState();
    controller.addListener(() {

    });
  }
  
    @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Container(
          child: PageView(
            controller: controller,
            children: imgUrlList
                  .map((item) => buildPageItem(imgUrlList.indexOf(item), item))
                  .toList(),
          ),
          width: 200,
          height: 200,
        ),
      ),
    );
  }
  
  Widget buildPageItem(int index, String imgUrl) {
    return Image.network(imgUrl);
  }
Copy the code

In the above code, I extrapolate PageView Item into a separate build method. On the one hand, the code is simplified, and different data can be accessed flexibly according to the situation, so as to unify the control style. This is also a practical skill in actual development.

Add special effects to PageView

The above is the basic use of PageView, familiar with the can directly from here to see, the following start to add special effects to PageView. This effect is essentially applied to the PageView Item, and we don’t have any extra processing for PageView. The basic idea is: 1. Listen to the scroll callback of PageView 2. Calculate the percentage of PageView scroll per frame relative to the width of PageView 3. Inform Item to be offset and scaled according to the calculated scroll percentage

You can see from the code above that we set the width height of the component to 200. We only use width 200 here, but you can set other widths as well. Get the PageView offset from the controller callback method and calculate the percentage:

  var pageOffset = 0.0;

  @override
  void initState() {
    super.initState();
    controller.addListener(() {
      setState(() {
        pageOffset = controller.offset / 200;
      });
    });
  }
Copy the code

Controller. offset gets the logical pixel value, which is 0 to 200*(n-1). Each page is incremented from 0 to 200, and the last page is 600. Divide this value by the width of 200 to get an offset percentage value in unit of page width, which is stored in the member variable pageOffset of _MyHomePageState for rendering Item use.

Next, Item rendering is carried out, which is also the core process of realizing special effects. Use the Transform Translate and scale methods, which are the offset and scale effects.

Nested these two effects into the outermost render of the item:

  Widget buildPageItem(int index, String imgUrl) {
    var currentLeftPageIndex = pageOffset.floor();
    var currentPageOffsetPercent = pageOffset - currentLeftPageIndex;
    return Transform.translate(
      offset: Offset((pageOffset - index) * 200.0),
      child: Transform.scale(
        scale: currentLeftPageIndex == index
            ? 1 - currentPageOffsetPercent
            : currentPageOffsetPercent,
        child: Image.network(imgUrl),
      ),
    );
  }
Copy the code

So that’s it? Yes, the core code is these lines, which may seem confusing at first glance, but let me explain them line by line. Pageoffset.floor () gets currentLeftPageIndex, which is the index of the item whose page-turning progress should be on the left of the page. If the default is page 0, half of page 0 is exposed as you turn to page 1, and only when you turn to page 1 is page 0 completely invisible. So currentLeftPageIndex here is page 0 from page 0 to page 1, page 1 from page 1 to page 2, page 2 from page 2 to page 3, and so on. PageOffset -CurrentLeftPageIndex gets the variable currentPageOffsetPercent. Subtract the global offset of PageView from the page number of the current left page to get the offset of the current left page. This value ranges from 0 to 1. So when you flip from page 0 to page 1, this variable is going to go from 0 to 1, and it’s going to go from 0 to 1 for every page that you flip. With the above two variables explained, the following is easy to understand. In order to realize the Item not moving, it is necessary to give an equivalent negative offset to the Item, that is, use transform. translate nesting and assign a horizontal offset value (pageoffset-index) * 200. When scaling, we need to consider two things. As mentioned above, we only look at two items in the page-turning process, so the scale value also needs to be based on currentLeftPageIndex == index to determine how the image is scaled. If currentLeftPageIndex == index is the Item on the left side of the render, or if it is the Item on the right side of the render, then use transform.scale to nest the component.

Now, if you look at this, you might be wondering, isn’t buildPageItem going to render all items? Why do you logically consider only the left and right items? Don’t the other items affect the layout? Here’s the answer: In the PageView component of Flutter, items outside offsets are not rendered. That is to say, page 2 and page 3 are not drawn from page 0 to page 1 in a normal PageView. This is not lazy loading because you are turning from page 2 to page 3. Page 0 and page 1 are also not drawn, regardless of whether the PageView Item should appear on the page after the offset. So the above logic makes sense. When we deal with the left and right items, we don’t need to worry about the rest of the items, because PageView already controls that they don’t appear on the page.

The offset component can also be implemented by using FractionalTranslation, which receives parameters based on its percentage and has the same effect:

    FractionalTranslation(
      translation: Offset(pageOffset - index, 0),
      child: Transform.scale(
        scale: currentLeftPageIndex == index
            ? 1 - currentPageOffsetPercent
            : currentPageOffsetPercent,
        child: Image.network(imgUrl),
      ),
    );
Copy the code

After this


More dry goods move to my personal blog www.nightfarmer.top/