如何在颤振中实现共面卡布局

时间:2020-03-16 21:11:59

标签: flutter-layout

我正在设法实现共面/不规则的卡片收集布局。这是关于材料设计文档https://imgur.com/miHhpFs

上的卡片布局的

我尝试使用GridView.count布局,但无法弄清楚如何取消项目。我还发现,有一些用户创建的库,例如https://pub.dev/packages/flutter_staggered_grid_view,可以为我想做的事情提供帮助,但是我更喜欢一种正式的解决方案,因为这种布局在材料设计文档中。

1 个答案:

答案 0 :(得分:0)

我不知道创建此文件的“官方解决方案”是什么,但我相信这将类似于“使用三个滚动控制器创建三个列表视图,其中一个与其他两个偏移,然后同步其滚动控制器,说明偏移量。”

我不知道这是否行得通,所以我创建了此dartpad进行测试: https://dartpad.dev/f9c8f00b78899d3c8c4a426d3466a8a3

以防万一飞镖无法正常工作,这是我使用的代码:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ScrollSync(),
    );
  }
}

class ScrollSync extends StatefulWidget {
  @override
  _ScrollSyncState createState() => _ScrollSyncState();
}

class _ScrollSyncState extends State<ScrollSync> {
  CustomScrollController _controller1 =
      CustomScrollController(keepScrollOffset: true);
  CustomScrollController _controller2 = CustomScrollController(
      initialScrollOffset: 150.0, keepScrollOffset: true);
  CustomScrollController _controller3 =
      CustomScrollController(keepScrollOffset: true);

  @override
  void initState() {
    _controller1.addListener(() =>
        _controller2.jumpToWithoutGoingIdleAndKeepingBallistic(
            _controller1.offset, "1 listen 2"));
    _controller1.addListener(() =>
        _controller3.jumpToWithoutGoingIdleAndKeepingBallistic(
            _controller1.offset, "1 listen 3"));
    _controller2.addListener(() =>
        _controller1.jumpToWithoutGoingIdleAndKeepingBallistic(
            _controller2.offset, "2 listen 1"));
    _controller2.addListener(() =>
        _controller3.jumpToWithoutGoingIdleAndKeepingBallistic(
            _controller2.offset, "2 listen 3"));
    _controller3.addListener(() =>
        _controller1.jumpToWithoutGoingIdleAndKeepingBallistic(
            _controller3.offset, "3 listen 1"));
    _controller3.addListener(() =>
        _controller2.jumpToWithoutGoingIdleAndKeepingBallistic(
            _controller3.offset, "3 listen 2"));

    super.initState();
  }

  @override
  void dispose() {
    _controller1.dispose();
    _controller2.dispose();
    _controller3.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Scroll Sync"),
      ),
      body: Row(
        children: [
          Flexible(
            flex: 1,
            child: ListView.builder(
              controller: _controller1,
              scrollDirection: Axis.vertical,
              itemBuilder: (_, index) => Container(
                color: Colors.blueGrey,
                width: 150,
                height: 300,
                margin: EdgeInsets.all(10.0),
                child: Center(
                  child: Text(
                    "$index",
                    textAlign: TextAlign.center,
                    style: Theme.of(context).textTheme.display2,
                  ),
                ),
              ),
            ),
          ),
          Flexible(
            flex: 1,
            child: ListView.builder(
              controller: _controller2,
              itemBuilder: (_, index) => Container(
                height: 300,
                color: Colors.blueGrey,
                margin: EdgeInsets.all(10.0),
                child: Center(
                  child: Text(
                    "$index",
                    style: Theme.of(context)
                        .textTheme
                        .display2
                        .copyWith(color: Colors.white),
                  ),
                ),
              ),
            ),
          ),
          Flexible(
            flex: 1,
            child: ListView.builder(
              controller: _controller3,
              itemBuilder: (_, index) => Container(
                height: 300,
                color: Colors.blueGrey,
                margin: EdgeInsets.all(10.0),
                child: Center(
                  child: Text(
                    "$index",
                    style: Theme.of(context)
                        .textTheme
                        .display2
                        .copyWith(color: Colors.black),
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class CustomScrollController extends ScrollController {
  CustomScrollController(
      {double initialScrollOffset = 0.0,
      keepScrollOffset = true,
      debugLabel,
      String controller})
      : super(
          initialScrollOffset: initialScrollOffset,
          keepScrollOffset: keepScrollOffset,
          debugLabel: debugLabel,
        );

  @override
  _SilentScrollPosition createScrollPosition(
    ScrollPhysics physics,
    ScrollContext context,
    ScrollPosition oldPosition,
  ) {
    return _SilentScrollPosition(
      physics: physics,
      context: context,
      oldPosition: oldPosition,
      initialPixels: initialScrollOffset,
    );
  }

  void jumpToWithoutGoingIdleAndKeepingBallistic(
      double value, String controller) {
    assert(positions.isNotEmpty, 'ScrollController not attached.');
    for (_SilentScrollPosition position
        in new List<ScrollPosition>.from(positions))
      position.jumpToWithoutGoingIdleAndKeepingBallistic(value, controller);
  }
}

class _SilentScrollPosition extends ScrollPositionWithSingleContext {
  _SilentScrollPosition({
    ScrollPhysics physics,
    ScrollContext context,
    ScrollPosition oldPosition,
    double initialPixels,
  }) : super(
          physics: physics,
          context: context,
          oldPosition: oldPosition,
          initialPixels: initialPixels,
        );

  void jumpToWithoutGoingIdleAndKeepingBallistic(
      double value, String controller) {
    print(controller);
    print(value);
    print(pixels);

    if (controller[0] == "2") {
      if (pixels + 150.0 != value) {
        forcePixels(value - 150.0);
      }
    } else if (controller[9] == "2") {
      if (pixels - 150.0 != value) {
        forcePixels(value + 150.0);
      }
    } else if (pixels != value) {
      forcePixels(value);
    }
  }
}