Flutter: Как создать двунаправленную прокрутку ListView с фиксированной частью слева и снизу - PullRequest
0 голосов
/ 28 февраля 2020

Как сделать прокрутку во флаттере, в которой левая и нижняя части экрана зафиксированы (оси), затем остальная часть экрана может быть прокручена горизонтально вправо или вертикально вверх. (представьте, что вы прокручиваете график с двумя осями .. см. изображение)

2D Graph

1 Ответ

0 голосов
/ 29 февраля 2020

Очень интересный вопрос. Просматривая документы, я не смог найти виджет, который бы подходил этому сценарию. Поэтому я решил немного поискать на pub.dev плагин, который мог бы это сделать.

Нашел: https://pub.dev/packages/bidirectional_scroll_view

Плагин работает довольно хорошо работа по прокрутке контента по обеим осям, но чтобы получить то, что вы ищете («фиксированная часть слева и снизу»), вам придется соответствующим образом структурировать свою страницу. Я решил go с виджетами Stack и Align, вот как это выглядит:

GIF result][6

См. Полный код на рабочем DartPad: https://dartpad.dev/10573c0e9bfa7f1f8212326b795d8628

Или взгляните на приведенный ниже код (не забудьте включить bidirectional_scroll_view в ваш проект):

void main() {
  runApp(new MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  BidirectionalScrollViewPlugin _plugin;
  double fixedAxisSpace = 100.0;
  double biDirectContentWidth = 4096.0;
  double biDirectContentHeight = 4096.0;

  @override
  void initState() {
    super.initState();
      _plugin = new BidirectionalScrollViewPlugin(
      child: _buildWidgets(),
      velocityFactor: 0.0, 
    );
  }

  void _snapToZeroZero(BuildContext context){
    double yOffset = biDirectContentHeight + fixedAxisSpace - context.size.height;
    _plugin.offset = new Offset(0, yOffset);
  }

  @override
  Widget build(BuildContext context) { 
    final btnSnapToZeroZero = Padding(
      padding: EdgeInsets.all(10.0),
        child:FlatButton(
          color: Colors.black,
          shape: RoundedRectangleBorder(
            borderRadius: new BorderRadius.circular(12.0),
          ),
          onPressed: () { _snapToZeroZero(context); },
          child: Text(
            "Snap to 0.0",
            textAlign: TextAlign.center,
            style: TextStyle(color: Colors.white),
            ),
       )
      );

    return new MaterialApp(
      debugShowCheckedModeBanner: false,
      home: new Scaffold(
        body: Stack(
          children: <Widget>[
             _plugin, // BidirectionalScrollViewPlugin, goes 1st because we want it to sit on the bottom layer
             Align( // Y Axis goes over _plugin, it is aligned to topLeft
              alignment: Alignment.topLeft,
              child: Column(
                children: <Widget> [
                  Expanded( 
                    child: Container(
                      width: fixedAxisSpace,
                      decoration: BoxDecoration(
                        color: Colors.white, // change to Colors.white70 too se what is going on "behind the scene"
                        border: Border(
                          right: BorderSide(width: 1.0, color: Colors.black),
                        ),
                      ),
                      child: Center(child: VerticalTextWidget("FIXED _ Y AXIS", 22))
                    ),
                  ),
                  SizedBox(height: fixedAxisSpace),
                ]
              ),
             ),
             Align( // X Axis goes over _plugin and Y Axis, it is aligned to bottomLeft
              alignment: Alignment.bottomLeft,
              child: Row(
                children: <Widget> [
                  SizedBox(width: fixedAxisSpace),
                  Expanded( 
                    child: Container(
                      height: fixedAxisSpace,
                      decoration: BoxDecoration(
                        color: Colors.white, // change to Colors.white70 too se what is going on "behind the scene"
                        border: Border(
                          top: BorderSide(width: 1.0, color: Colors.black),
                        ),
                      ),
                      child: Center(child: Text("FIXED | X AXIS", style: TextStyle(fontSize: 22)))
                    ),
                  ),
                ]
              ),
             ),

             Align( // this little square is optional, I use it to put a handy little button over everything else at the bottom left corner.
              alignment: Alignment.bottomLeft,
                child: Container(
                  color: Colors.white, // change to Colors.white70 too se what is going on "behind the scene"
                  height: fixedAxisSpace,
                  width: fixedAxisSpace,
                  child: btnSnapToZeroZero
              ),
             ),

          ],
        )
     )
    );
  }

  // put your large bidirectional content here
  Widget _buildWidgets() {
    return new Padding(
      padding: EdgeInsets.fromLTRB(100, 0, 0, 100),
      child: SizedBox(
        width: biDirectContentWidth,
        height: biDirectContentHeight,
        child: Image.network(
          'https://i.stack.imgur.com/j1ItQ.png?s=328&g=1',
          repeat: ImageRepeat.repeat,
          alignment: Alignment.bottomLeft
        ),
      )
    );
  }
}

VerticalTextWidget:

class VerticalTextWidget extends StatelessWidget {
  final String text;
  final double size;

  const VerticalTextWidget(this.text, this.size);

  @override
  Widget build(BuildContext context) {
    return Wrap(
      direction: Axis.vertical,
      alignment: WrapAlignment.center,
      children: text.split("").map((string) => Text(string, style: TextStyle(fontSize: size))).toList(),
    );
  }
}
...