ListView Scroll with AppBar Scroll In Flutter

ListView Scroll with AppBar Scroll In Flutter :

Screenshot :

animateOpacityWidget.dart

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

class AnimateOpacityWidget extends StatefulWidget {
  AnimateOpacityWidget(
      {@required this.opacity, @required this.child, this.controller});

  final double opacity;
  final Widget child;
  final ScrollController controller;

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

class AnimateOpacityWidgetState extends State<AnimateOpacityWidget> {
  double _opacity;

  @override
  void initState() {
    _opacity = widget.opacity;
    widget.controller.addListener(onScroll);
    super.initState();
  }

  void onScroll() {
    final num opacity = widget.controller.offset * 0.01;
    if (opacity >= 0 && opacity <= 1) {
      setState(() {
        _opacity = opacity;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Opacity(opacity: _opacity, child: widget.child),
    );
  }

  @override
  void dispose() {
    super.dispose();
    widget.controller.removeListener(onScroll);
  }
}

render.dart

import 'dart:math' show min, max;
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';

typedef void RenderCustomListViewCallback(double stuckAmount);

class CustomListViewRenderer extends RenderBox
    with
        ContainerRenderObjectMixin<RenderBox, MultiChildLayoutParentData>,
        RenderBoxContainerDefaultsMixin<RenderBox, MultiChildLayoutParentData> {
  CustomListViewRenderer({
    @required ScrollableState scrollable,
    RenderCustomListViewCallback callback,
    bool overlapHeaders = false,
    RenderBox header,
    RenderBox content,
  })  : assert(scrollable != null),
        _scrollable = scrollable,
        _callback = callback,
        _overlapHeaders = overlapHeaders {
    if (content != null) 
      add(content);
    if (header != null) 
      add(header);
  }

  RenderCustomListViewCallback _callback;
  ScrollableState _scrollable;
  bool _overlapHeaders;

  set scrollable(ScrollableState newValue) {
    assert(newValue != null);
    if (_scrollable == newValue) {
      return;
    }
    final ScrollableState oldValue = _scrollable;
    _scrollable = newValue;
    markNeedsLayout();
    if (attached) {
      oldValue.position?.removeListener(markNeedsLayout);
      newValue.position?.addListener(markNeedsLayout);
    }
  }

  set callback(RenderCustomListViewCallback newValue) {
    if (_callback == newValue) {
      return;
    }
    _callback = newValue;
    markNeedsLayout();
  }

  set overlapHeaders(bool newValue) {
    if (_overlapHeaders == newValue) {
      return;
    }
    _overlapHeaders = newValue;
    markNeedsLayout();
  }

  @override
  void attach(PipelineOwner owner) {
    super.attach(owner);
    _scrollable.position?.addListener(markNeedsLayout);
  }

  @override
  void detach() {
    _scrollable.position?.removeListener(markNeedsLayout);
    super.detach();
  }

  // short-hand to access the child RenderObjects
  RenderBox get _headerBox => lastChild;

  RenderBox get _contentBox => firstChild;

  @override
  void performLayout() {
    // ensure we have header and content boxes
    assert(childCount == 2);

    // layout both header and content widget
    final BoxConstraints childConstraints = constraints.loosen();
    _headerBox.layout(childConstraints, parentUsesSize: true);
    _contentBox.layout(childConstraints, parentUsesSize: true);

    final num headerHeight = _headerBox.size.height;
    final num contentHeight = _contentBox.size.height;

    // determine size of ourselves based on content widget
    final num width = max(constraints.minWidth, _contentBox.size.width);
    final num height = max(constraints.minHeight,
        _overlapHeaders ? contentHeight : headerHeight + contentHeight);
    size = Size(width, height);
    assert(size.width == constraints.constrainWidth(width));
    assert(size.height == constraints.constrainHeight(height));
    assert(size.isFinite);

    // place content underneath header
    final MultiChildLayoutParentData contentParentData =
        _contentBox.parentData;
    contentParentData.offset =
        Offset(0.0, _overlapHeaders ? 0.0 : headerHeight);

    // determine by how much the header should be stuck to the top
    final double stuckOffset = determineStuckOffset();

    // place header over content relative to scroll offset
    final double maxOffset = height - headerHeight;
    final MultiChildLayoutParentData headerParentData =
        _headerBox.parentData;
    headerParentData.offset =
        Offset(0.0, max(0.0, min(-stuckOffset, maxOffset)));

    // report to widget how much the header is stuck.
    if (_callback != null) {
      final num stuckAmount =
          max(min(headerHeight, stuckOffset), -headerHeight) / headerHeight;
      _callback(stuckAmount);
    }
  }

  double determineStuckOffset() {
    final RenderObject scrollBox = _scrollable.context.findRenderObject();
    if (scrollBox?.attached ?? false) {
      try {
        return localToGlobal(Offset.zero, ancestor: scrollBox).dy;
      } catch (e) {
        // ignore and fall-through and return 0.0
      }
    }
    return 0.0;
  }

  @override
  void setupParentData(RenderObject child) {
    super.setupParentData(child);
    if (child.parentData is! MultiChildLayoutParentData) {
      child.parentData = MultiChildLayoutParentData();
    }
  }

  @override
  double computeMinIntrinsicWidth(double height) {
    return _contentBox.getMinIntrinsicWidth(height);
  }

  @override
  double computeMaxIntrinsicWidth(double height) {
    return _contentBox.getMaxIntrinsicWidth(height);
  }

  @override
  double computeMinIntrinsicHeight(double width) {
    return _overlapHeaders
        ? _contentBox.getMinIntrinsicHeight(width)
        : (_headerBox.getMinIntrinsicHeight(width) +
            _contentBox.getMinIntrinsicHeight(width));
  }

  @override
  double computeMaxIntrinsicHeight(double width) {
    return _overlapHeaders
        ? _contentBox.getMaxIntrinsicHeight(width)
        : (_headerBox.getMaxIntrinsicHeight(width) +
            _contentBox.getMaxIntrinsicHeight(width));
  }

  @override
  double computeDistanceToActualBaseline(TextBaseline baseline) {
    return defaultComputeDistanceToHighestActualBaseline(baseline);
  }

  @override
  bool hitTestChildren(HitTestResult result, {Offset position}) {
    return defaultHitTestChildren(result, position: position);
  }

  @override
  bool get isRepaintBoundary => true;

  @override
  void paint(PaintingContext context, Offset offset) {
    defaultPaint(context, offset);
  }
}

widget.dart

import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import './render.dart';

typedef Widget StickyHeaderWidgetBuilder(
    BuildContext context, double stuckAmount);

class CustomListView extends MultiChildRenderObjectWidget {
  /// Constructs a [CustomListView] widget.
  CustomListView({
    Key key,
    @required this.header,
    @required this.content,
    this.overlapHeaders = false,
    this.callback,
  }) : super(
          key: key,
          children: <Widget>[content, header],
        );

  /// Header to be shown at the top of the parent [Scrollable] content.
  final Widget header;

  /// Content to be shown below the header.
  final Widget content;

  /// If true, the header will overlap the Content.
  final bool overlapHeaders;

  /// Optional callback with stickyness value. If you think you need this, then you might want to
  /// consider using [CustomListViewBuilder] instead.
  final RenderCustomListViewCallback callback;

  @override
  CustomListViewRenderer createRenderObject(BuildContext context) {
    final ScrollableState scrollable = Scrollable.of(context);
    assert(scrollable != null);
    return CustomListViewRenderer(
      scrollable: scrollable,
      callback: callback,
      overlapHeaders: overlapHeaders,
    );
  }

  @override
  void updateRenderObject(
      BuildContext context, CustomListViewRenderer renderObject) {
    renderObject
      ..scrollable = Scrollable.of(context)
      ..callback = callback
      ..overlapHeaders = overlapHeaders;
  }
}

/// Sticky Header Builder Widget.
///
/// The same as [CustomListView] but instead of supplying a Header view, you supply a [builder] that
/// constructs the header with the appropriate stickyness.
///
/// Place this widget inside a [ListView], [GridView], [CustomScrollView], [SingleChildScrollView] or similar.
///
class CustomListViewBuilder extends StatefulWidget {
  /// Constructs a [CustomListViewBuilder] widget.
  const CustomListViewBuilder({
    Key key,
    @required this.builder,
    this.content,
    this.overlapHeaders = false,
  }) : super(key: key);

  /// Called when the sticky amount changes for the header.
  /// This builder must not return null.
  final StickyHeaderWidgetBuilder builder;

  /// Content to be shown below the header.
  final Widget content;

  /// If true, the header will overlap the Content.
  final bool overlapHeaders;

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

class _CustomListViewBuilderState extends State<CustomListViewBuilder> {
  double _stuckAmount;

  @override
  Widget build(BuildContext context) {
    return CustomListView(
      overlapHeaders: widget.overlapHeaders,
      header: LayoutBuilder(
        builder: (BuildContext context, _) => widget.builder(context, _stuckAmount ?? 0.0),
      ),
      content: widget.content,
      callback: (double stuckAmount) {
        if (_stuckAmount != stuckAmount) {
          _stuckAmount = stuckAmount;
          WidgetsBinding.instance.endOfFrame.then((_) {
            if (mounted) {
              setState(() {});
            }
          });
        }
      },
    );
  }
}

main.dart

import 'package:flutter/material.dart';
import 'animateOpacityWidget.dart';
import 'widget.dart';

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

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

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> {
  final dynamic controller = ScrollController();
  double cOpacity = 0.0;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
          resizeToAvoidBottomPadding: true,
          appBar: PreferredSize(
              preferredSize: Size.fromHeight(60.0),
              child: Container(
                decoration: BoxDecoration(boxShadow: <BoxShadow>[
                  BoxShadow(
                    color: const Color.fromRGBO(0, 116, 228, 1),
                    offset: const Offset(0, 2.0),
                    blurRadius: 0.25,
                  )
                ]),
                child: AppBar(
                  elevation: 0.0,
                  backgroundColor: const Color.fromRGBO(0, 116, 228, 1),
                  title: AnimateOpacityWidget(
                      controller: controller,
                      opacity: cOpacity,
                      child: Text('Flutter Tutorial',
                          style: TextStyle(fontSize: 18))),
                  actions: <Widget>[
                    Padding(
                      padding: const EdgeInsets.fromLTRB(0, 0, 10, 0),
                      child: Container(
                        height: 40,
                        width: 40,
                        child: IconButton(
                          icon: Icon(Icons.settings, color: Colors.white),
                          onPressed: () {},
                        ),
                      ),
                    ),
                  ],
                ),
              )),
          body: Scaffold(
            backgroundColor: const Color.fromRGBO(0, 116, 228, 1),
            body: ListView.builder(
                controller: controller,
                physics: const ClampingScrollPhysics(),
                itemCount: 2,
                itemBuilder: (BuildContext context, num index) {
                  return Material(
                    color: const Color.fromRGBO(0, 116, 228, 1),
                    child: CustomListView(
                      header: Container(
                        color: const Color.fromRGBO(0, 116, 228, 1),
                        child: Column(
                          children: <Widget>[
                            index != 0
                                ? Container(
                                    color: const Color.fromRGBO(0, 116, 228, 1),
                                    height: 100.0,
                                    padding: const EdgeInsets.symmetric(horizontal: 0.0),
                                    alignment: Alignment.centerLeft,
                                    child: Column(
                                      crossAxisAlignment: CrossAxisAlignment.stretch,
                                      children: <Widget>[
                                        Padding(
                                          padding: const EdgeInsets.fromLTRB(20, 20, 20, 0),
                                          child: Container(
                                              height: 50,
                                              child: Column(
                                                crossAxisAlignment:
                                                    CrossAxisAlignment.center,
                                                children: <Widget>[
                                                  Container(
                                                    height: 45,
                                                    width: double.infinity,
                                                    decoration: BoxDecoration(
                                                        color: Colors.white,
                                                        borderRadius: const BorderRadius.all(Radius.circular(5.0))),
                                                    child: Padding(padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
                                                      child: Container(
                                                        child: TextField(
                                                            onChanged: (String value) {},
                                                            onEditingComplete: () {},
                                                            decoration: InputDecoration(
                                                                labelStyle: const TextStyle(fontFamily: 'MontserratMedium'),
                                                                hintText: 'Search', border: InputBorder.none,
                                                                hintStyle: TextStyle(fontSize: 15, color: Colors.grey),
                                                                prefixIcon: Icon(Icons.search, color: Colors.grey))),
                                                      ),
                                                    ),
                                                  ),
                                                ],
                                              )),
                                        ),
                                        Padding(
                                          padding: const EdgeInsets.fromLTRB(0, 10, 0, 0),
                                          child: Container(
                                              height: 20,
                                              width: double.infinity,
                                              decoration: BoxDecoration(
                                                  color: Colors.white,
                                                  border: Border.all(color: Colors.white),
                                                  borderRadius:
                                                      const BorderRadius.only(topLeft: Radius.circular(12.0), topRight: Radius.circular(12.0)),
                                                  boxShadow: <BoxShadow>[
                                                    BoxShadow(color: Colors.white, offset: const Offset(0, 2.0), blurRadius: 0.25,
                                                    )
                                                  ])),
                                        ),
                                      ],
                                    ))
                                : Container(height: 0, color: Colors.white),
                          ],
                        ),
                      ),
                      content: Container(
                        color: Colors.white,
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          mainAxisAlignment: MainAxisAlignment.start,
                          children: <Widget>[
                            Container(
                              color: const Color.fromRGBO(0, 116, 228, 1),
                              width: double.infinity,
                              child: index == 0
                                  ? Padding(padding: const EdgeInsets.fromLTRB(20, 0, 0, 0),
                                      child: Column(crossAxisAlignment: CrossAxisAlignment.start,
                                        children: <Widget>[
                                          Padding(
                                              padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
                                              child: Text(
                                                  'Flutter Tutorial World Best WebSite',
                                                  style: TextStyle(color: Colors.white, fontSize: 20, letterSpacing: 0.53, fontWeight: FontWeight.bold))),
                                          Padding(
                                            padding: const EdgeInsets.fromLTRB(0, 12, 0, 0),
                                            child: Text('www.fluttertutorial.in',
                                                style: TextStyle(color: Colors.white, fontSize: 14, letterSpacing: 0.26, fontWeight: FontWeight.normal)),
                                          ),
                                        ],
                                      ))
                                  : Container(
                                      color: Colors.white,
                                      child: Padding(padding: const EdgeInsets.fromLTRB(0, 0, 0, 20),
                                        child: Column(crossAxisAlignment: CrossAxisAlignment.start,
                                            children: getListViewChildrens()),
                                      )),
                            ),
                          ],
                        ),
                      ),
                    ),
                  );
                }),
          )),
    );
  }

  List<Widget> getListViewChildrens() {
    List<Widget> items;
    items = <Widget>[];
    for (int i = 0; i < 100; i++) {
      items.add(
        Padding(
          padding: const EdgeInsets.fromLTRB(0, 2, 0, 0),
          child: Container(
              padding: EdgeInsets.all(10), child: (Text(i.toString()))),
        ),
      );
      if (i != 100 - 1) {
        items.add(const Divider(height: 15.0));
      }
    }

    return items;
  }
}

The flutter tutorial  is a website that bring you the latest and amazing resources of code. All the languages codes are included in this website. The languages like flutter, android, java,kotlin etc.with the help of this languages any user can develop the beautiful application

For more information about Flutter. visit www.fluttertutorial.in