как найти элемент в какой-то точке в ListView во Flutter?

Aug 18 2020

Мне нужно найти ниже точечный элемент / индекс в ListView? Возможно ли это получить? любой ListView.Builder / CustomScrollView / .... при прокрутке элемент / индекс должен обновляться в реальном времени. пример: 'Center index is $ index'

положение этой красной линии может измениться. может быть по центру, может быть, сверху / снизу / 0,25 / 0,75 ...

а также высота элемента может быть фиксированной / может быть гибкой

Ответы

1 chunhunghan Aug 18 2020 at 14:22

Вы можете скопировать и вставить полный код ниже.
Вы можете использовать пакет.https://pub.dev/packages/inview_notifier_list
С атрибутом isInViewPortConditionи элементом обтекания InViewNotifierWidget, этот допустимый размер является гибким, см. Рабочую демонстрацию
Для CustomScrollViewиспользования InViewNotifierCustomScrollView, пример здесьhttps://github.com/rvamsikrishna/inview_notifier_list/blob/master/example/lib/csv_example.dart
фрагмент кода

isInViewPortCondition:
          (double deltaTop, double deltaBottom, double viewPortDimension) {
        return deltaTop < (0.5 * viewPortDimension) &&
            deltaBottom > (0.5 * viewPortDimension);
      },
...
InViewNotifierWidget(
        id: '$index', builder: (BuildContext context, bool isInView, Widget child) { if (isInView) { inviewIndex = index.toString(); callback(); } return Container( height: index.isEven? 50 : 150 , width: double.infinity, color: Colors.blue, child: Text("$index ${isInView.toString()}"));
        },
      );

полный код

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(
        title: "test",
      ),
    );
  }
}

String inviewIndex = "";

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  refresh() {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      setState(() {});
    });
  }

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text("Center index is $inviewIndex"),
            Expanded(
                child: ImageList(
              callback: refresh,
            ))
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

class ImageList extends StatelessWidget {
  VoidCallback callback;
  ImageList({this.callback});

  @override
  Widget build(BuildContext context) {
    return Stack(
      fit: StackFit.expand,
      children: <Widget>[
        InViewNotifierList(
          scrollDirection: Axis.vertical,
          initialInViewIds: ['0'],
          isInViewPortCondition:
              (double deltaTop, double deltaBottom, double viewPortDimension) {
            return deltaTop < (0.5 * viewPortDimension) &&
                deltaBottom > (0.5 * viewPortDimension);
          },
          itemCount: 10,
          builder: (BuildContext context, int index) {
            return Container(
              width: double.infinity,
              height: 100.0,
              alignment: Alignment.center,
              margin: EdgeInsets.symmetric(vertical: 50.0),
              child: LayoutBuilder(
                builder: (BuildContext context, BoxConstraints constraints) {
                  return InViewNotifierWidget(
                    id: '$index', builder: (BuildContext context, bool isInView, Widget child) { if (isInView) { inviewIndex = index.toString(); callback(); } return Container( height: index.isEven? 50 : 150 , width: double.infinity, color: Colors.blue, child: Text("$index ${isInView.toString()}"));
                    },
                  );
                },
              ),
            );
          },
        ),
        Align(
          alignment: Alignment.center,
          child: Container(
            height: 1.0,
            color: Colors.redAccent,
          ),
        )
      ],
    );
  }
}
2 JigarPatel Aug 18 2020 at 13:06

Один из способов сделать это - использовать ScrollControllerи NotificationListener.

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  ScrollController sc = ScrollController();
  int centerItemIndex;
  double screenHeight;

  @override
  Widget build(BuildContext context) {
    
    screenHeight = MediaQuery.of(context).size.height;
    if(centerItemIndex == null){
      centerItemIndex = ((screenHeight/2)/50).floor();    //50 is the height of 1 item
      print('center item = $centerItemIndex'); } return Stack( children: [ NotificationListener( child: ListView.builder( controller: sc, itemCount: 50, itemBuilder: (ctx, index) { return Container( height: 50, color: Colors.green, child: Center(child: Text('$index')),
              );
            },
          ),
          onNotification: (_) {
            int calculatedIndex = ((sc.position.pixels + screenHeight/2)/50).floor();
            if(calculatedIndex != centerItemIndex){
              centerItemIndex = calculatedIndex;
              print('center item = $centerItemIndex');
            }
            return true;
          },
        ),
        Center(
          child: Container(
            height: 2,
            color: Colors.red,
          ),
        ),
      ],
    );
  }
}
1 MAlkhatib Aug 18 2020 at 14:00

Решение здесь https://github.com/google/flutter.widgets

попробуйте использовать: scrollable_positioned_list