flutter 如何使滚动条始终可见?

c9qzyr3d  于 2022-12-14  发布在  Flutter
关注(0)|答案(6)|浏览(352)

我如何在用户开始滚动之前使滚动条可见。下面是我如何创建列表的代码

Scrollbar(
           child: ListView.separated(
                    itemBuilder: 
                    (ctx,index){
                    return ListTile( 
                        isThreeLine: false,
                        dense: true,
                        leading: IconButton(
                          icon: Icon(Icons.location_on),
                          onPressed: null,

                          ),
                        title: Text(bList[index].bName),
                        onTap: ()=>_homescreen(bList[index].bId,bList[index].bName,index),
                      );
                  },
                    separatorBuilder: (context,index) => Divider(color: Colors.black,),
                    itemCount: bList.length == null ? 0 : bList.length),
                )

将列表包裹在滚动条中,我可以在滚动时看到滚动条。但是有没有可能使滚动条一直可见?提前感谢。

pnwntuvh

pnwntuvh1#

可以如下所示将ListView Package 为Scrollbar。当isAlwaysShown为true时,必须传递附加到滚动视图的控制器

Scrollbar(
          controller: ScrollController(),
          isAlwaysShown: true,
          child: ListView...
rjzwgtxy

rjzwgtxy2#

演示:DartPad
您可以使用ScrollbarPainter。然后使用AlwaysStoppedAnimation<double>(1.0)使其始终可见,使用ScrollNotification更新滚动位置。

  • 我的滚动条.dart*
import 'package:flutter/material.dart';

const double _kScrollbarThickness = 6.0;

class MyScrollbar extends StatefulWidget {
  final ScrollableWidgetBuilder builder;
  final ScrollController scrollController;

  const MyScrollbar({
    Key key,
    this.scrollController,
    @required this.builder,
  })  : assert(builder != null),
        super(key: key);

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

class _MyScrollbarState extends State<MyScrollbar> {
  ScrollbarPainter _scrollbarPainter;
  ScrollController _scrollController;
  Orientation _orientation;

  @override
  void initState() {
    super.initState();
    _scrollController = widget.scrollController ?? ScrollController();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _updateScrollPainter(_scrollController.position);
    });
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _scrollbarPainter = _buildMaterialScrollbarPainter();
  }

  @override
  void dispose() {
    _scrollbarPainter.dispose();
    super.dispose();
  }

  ScrollbarPainter _buildMaterialScrollbarPainter() {
    return ScrollbarPainter(
      color: Theme.of(context).highlightColor.withOpacity(1.0),
      textDirection: Directionality.of(context),
      thickness: _kScrollbarThickness,
      fadeoutOpacityAnimation: const AlwaysStoppedAnimation<double>(1.0),
      padding: MediaQuery.of(context).padding,
    );
  }

  bool _updateScrollPainter(ScrollMetrics position) {
    _scrollbarPainter.update(
      position,
      position.axisDirection,
    );
    return false;
  }

  @override
  void didUpdateWidget(MyScrollbar oldWidget) {
    super.didUpdateWidget(oldWidget);
    _updateScrollPainter(_scrollController.position);
  }

  @override
  Widget build(BuildContext context) {
    return OrientationBuilder(
      builder: (context, orientation) {
        _orientation ??= orientation;
        if (orientation != _orientation) {
          _orientation = orientation;
          _updateScrollPainter(_scrollController.position);
        }
        return NotificationListener<ScrollNotification>(
          onNotification: (notification) =>
              _updateScrollPainter(notification.metrics),
          child: CustomPaint(
            painter: _scrollbarPainter,
            child: widget.builder(context, _scrollController),
          ),
        );
      },
    );
  }
}

**用法:**main.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

import 'MyScrollbar.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Home(),
    );
  }
}

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: MyScrollbar(
        //scrollController: ctrl, //You can assign your scroll controller here or ignore
        builder: (context, scrollController) => ListView.builder(
          controller: scrollController, //should scrollController from callback
          itemCount: 30,
          itemBuilder: (context, index) => ListTile(
            title: Text("Index $index"),
          ),
        ),
      ),
    );
  }
}

备注:

如果ListView小工具中的一个子小工具是Stateful小工具,并且动态更改其大小,则MyScrollbar可能不会得到更新。

col17t5w

col17t5w3#

您可以使用可拖动滚动条包https://pub.dev/packages/draggable_scrollbar来实现这一点。
有很多可能性,你可以用alwaysVisibleScrollThumb: true,来显示它
您可以执行以下示例:

DraggableScrollbar.rrect(
  controller: myScrollController,
  child: ListView.builder(
    controller: myScrollController,
    itemCount: 1000,
    itemExtent: 100.0,
    itemBuilder: (context, index) {
      return Container(
        padding: EdgeInsets.all(8.0),
        child: Material(
          elevation: 4.0,
          borderRadius: BorderRadius.circular(4.0),
          color: Colors.green[index % 9 * 100],
          child: Center(
            child: Text(index.toString()),
          ),
        ),
      );
    },
  ),
);

另一种方法,源自@slightfoot:https://gist.github.com/slightfoot/beb74749bf2e743a6da294b37a7dcf8d
您可以使用自定义滚动绘制来访问始终可见的滚动条,如以下示例代码所示:

import 'package:flutter/gestures.dart' show DragStartBehavior;
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

void main() {
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor: Colors.indigo,
        accentColor: Colors.pinkAccent,
      ),
      home: ExampleScreen(),
    ),
  );
}

class ExampleScreen extends StatefulWidget {
  @override
  _ExampleScreenState createState() => _ExampleScreenState();
}

class _ExampleScreenState extends State<ExampleScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('SingleChildScrollView With Scrollbar'),
      ),
      body: 
      Container(
        height: MediaQuery.of(context).size.height * 0.3,
        child:
      SingleChildScrollViewWithScrollbar(
        scrollbarColor: Theme.of(context).accentColor.withOpacity(0.75),
        scrollbarThickness: 8.0,
        child: Container(
          //height: 1500,
          child: ListView(
              shrinkWrap: true,
              children: <Widget>[
                ListTile(title: Text('Item 1')),
                ListTile(title: Text('Item 2')),
                ListTile(title: Text('Item 3')),
                ListTile(title: Text('Item 4')),
                ListTile(title: Text('Item 5')),
                ListTile(title: Text('Item 6')),
                ListTile(title: Text('Item 1')),
                ListTile(title: Text('Item 2')),
                ListTile(title: Text('Item 3')),
                ListTile(title: Text('Item 4')),
                ListTile(title: Text('Item 5')),
                ListTile(title: Text('Item 6')),
              ],
            ),
        ),
      ),),
    );
  }
}

class SingleChildScrollViewWithScrollbar extends StatefulWidget {
  const SingleChildScrollViewWithScrollbar({
    Key key,
    this.scrollDirection = Axis.vertical,
    this.reverse = false,
    this.padding,
    this.primary,
    this.physics,
    this.controller,
    this.child,
    this.dragStartBehavior = DragStartBehavior.down,
    this.scrollbarColor,
    this.scrollbarThickness = 6.0,
  }) : super(key: key);

  final Axis scrollDirection;
  final bool reverse;
  final EdgeInsets padding;
  final bool primary;
  final ScrollPhysics physics;
  final ScrollController controller;
  final Widget child;
  final DragStartBehavior dragStartBehavior;
  final Color scrollbarColor;
  final double scrollbarThickness;

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

class _SingleChildScrollViewWithScrollbarState extends State<SingleChildScrollViewWithScrollbar> {
  AlwaysVisibleScrollbarPainter _scrollbarPainter;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    rebuildPainter();
  }

  @override
  void didUpdateWidget(SingleChildScrollViewWithScrollbar oldWidget) {
    super.didUpdateWidget(oldWidget);
    rebuildPainter();
  }

  void rebuildPainter() {
    final theme = Theme.of(context);
    _scrollbarPainter = AlwaysVisibleScrollbarPainter(
      color: widget.scrollbarColor ?? theme.highlightColor.withOpacity(1.0),
      textDirection: Directionality.of(context),
      thickness: widget.scrollbarThickness,
    );
  }

  @override
  void dispose() {
    _scrollbarPainter?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return RepaintBoundary(
      child: CustomPaint(
        foregroundPainter: _scrollbarPainter,
        child: RepaintBoundary(
          child: SingleChildScrollView(
            scrollDirection: widget.scrollDirection,
            reverse: widget.reverse,
            padding: widget.padding,
            primary: widget.primary,
            physics: widget.physics,
            controller: widget.controller,
            dragStartBehavior: widget.dragStartBehavior,
            child: Builder(
              builder: (BuildContext context) {
                _scrollbarPainter.scrollable = Scrollable.of(context);
                return widget.child;
              },
            ),
          ),
        ),
      ),
    );
  }
}

class AlwaysVisibleScrollbarPainter extends ScrollbarPainter {
  AlwaysVisibleScrollbarPainter({
    @required Color color,
    @required TextDirection textDirection,
    @required double thickness,
  }) : super(
          color: color,
          textDirection: textDirection,
          thickness: thickness,
          fadeoutOpacityAnimation: const AlwaysStoppedAnimation(1.0),
        );

  ScrollableState _scrollable;

  ScrollableState get scrollable => _scrollable;

  set scrollable(ScrollableState value) {
    _scrollable?.position?.removeListener(_onScrollChanged);
    _scrollable = value;
    _scrollable?.position?.addListener(_onScrollChanged);
    _onScrollChanged();
  }

  void _onScrollChanged() {
    update(_scrollable.position, _scrollable.axisDirection);
  }

  @override
  void dispose() {
    _scrollable?.position?.removeListener(notifyListeners);
    super.dispose();
  }
}
gwbalxhn

gwbalxhn4#

切换到Flutter分支主
添加始终显示:滚动条中的false
Ref

eh57zj3b

eh57zj3b5#

将小部件 Package 在滚动条小部件中

cld4siwp

cld4siwp6#

对于始终可见的滚动条或原始滚动条,请使用thumbVisibility:false(始终显示:true已过时)

使用滚动条对于自定义滚动条,

**ScrollController controller = ScrollController(); // Mandatory: ScrollController**

Scrollbar(
  controller: controller, // Mandatory: ScrollController
  // isAlwaysShown: true,  // deprecated
  thumbVisibility: true,  // For always showing Scroll Bar: Use this
  thickness: 10,  // Optional: Thickness
  radius: Radius.circular(5), // Optional: Radius
  child: ListView.builder(
    itemCount: 100,
    controller: controller, // Mandatory: ScrollController
    itemBuilder: (context, index){
      return ListTile(
        leading: CircleAvatar(
          backgroundColor: Colors.deepOrangeAccent,
          child: FlutterLogo(),
        ),
        title: Text('Title-$index'),
        subtitle: Text('Subtitle-$index'),
        trailing: CircleAvatar(
          child: Icon(Icons.navigate_next),
          backgroundColor: Colors.deepOrangeAccent,
        ),
      );
    },
  ),
),

使用RawScrollbar对于自定义滚动条,

ScrollController controller = ScrollController(); // Mandatory: ScrollController

RawScrollbar(
  controller: controller, // Mandatory: ScrollController
  // isAlwaysShown: true,  // deprecated
  thumbVisibility: true,  // For always showing Scroll Bar: Use this
  thickness: 10,  // Optional: Thickness
  thumbColor: Colors.greenAccent, // Optional: Color
  radius: Radius.circular(5), // Optional: Radius
  child: ListView.builder(
    itemCount: 100,
    controller: controller, // Mandatory: ScrollController
    itemBuilder: (context, index){
      return ListTile(
        leading: CircleAvatar(
          backgroundColor: Colors.deepOrangeAccent,
          child: FlutterLogo(),
        ),
        title: Text('Title-$index'),
        subtitle: Text('Subtitle-$index'),
        trailing: CircleAvatar(
          child: Icon(Icons.navigate_next),
          backgroundColor: Colors.deepOrangeAccent,
        ),
      );
    },
  ),
)

相关问题