Flutter: How to create bidirectional scrolling ListView with fixed portion of on left and bottom

1,058

Very interesting question. After looking through the docs I couldn't find a widget that would fit this scenario. So I decided to search a bit on pub.dev for a plugin that could make this happen.

Found it: https://pub.dev/packages/bidirectional_scroll_view

The plugin does a fairly good job of scrolling content on both axis, but to get what you are looking for ("fixed portion of on left and bottom") you are gonna have to structure your page accordingly. I decided to go with Stack and Align widgets, here is what it looks like:

GIF result][6

See the full code on a working DartPad: https://dartpad.dev/10573c0e9bfa7f1f8212326b795d8628

Or take a look at the code bellow (don't forget to include bidirectional_scroll_view in your project):

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(),
    );
  }
}
Share:
1,058
DeepMind
Author by

DeepMind

Updated on December 18, 2022

Comments

  • DeepMind
    DeepMind over 1 year

    How can I make a scrolling view in Flutter in which a left and bottom portion of the screen is fixed (axes), then the rest of the screen can be scrolled horizontally to the right, or vertically upward. (imagine scrolling a graph with two axes .. see image)

    2D Graph