From b9fe47e6ce081d7e7df0aeb412491e5f85e17fda Mon Sep 17 00:00:00 2001 From: deepikahr Date: Mon, 21 Sep 2020 18:29:41 +0530 Subject: [PATCH 01/13] alignment feature added --- README.md | 7 + example/lib/main_temp.dart | 460 +++++++ .../sticky_header/gf_sticky_header.dart | 1187 +++++++++++++++++ lib/getwidget.dart | 1 + 4 files changed, 1655 insertions(+) create mode 100644 example/lib/main_temp.dart create mode 100644 lib/components/sticky_header/gf_sticky_header.dart diff --git a/README.md b/README.md index 2b365a03..bdf2b718 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ +}=- m +0[ + [![pub package](https://img.shields.io/pub/v/getwidget.svg)](https://pub.dartlang.org/packages/getwidget) [![Build Status](https://travis-ci.org/ionicfirebaseapp/getwidget.svg?branch=master)](https://travis-ci.com/ionicfirebaseapp/getwidget) [![Open Source Love](https://badges.frapsoft.com/os/v1/open-source.svg?v=102)](https://opensource.org/licenses/MIT) [![License](https://img.shields.io/badge/license-MIT-orange.svg)](https://github.com/ionicfirebaseapp/getwidget/blob/master/LICENSE) [![Twitter Follow](https://img.shields.io/twitter/follow/getwidgetdev.svg?style=social)](https://twitter.com/getwidgetdev) +":????????????//// ;/p[[[[`" + +

GetWidget @@ -28,6 +34,7 @@

+ ## Quick start Read the [Getting started page](https://docs.getwidget.dev) diff --git a/example/lib/main_temp.dart b/example/lib/main_temp.dart new file mode 100644 index 00000000..800bd766 --- /dev/null +++ b/example/lib/main_temp.dart @@ -0,0 +1,460 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:getwidget/getwidget.dart'; + +final List imageList = [ + 'https://cdn.pixabay.com/photo/2017/12/03/18/04/christmas-balls-2995437_960_720.jpg', + 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', + 'https://cdn.pixabay.com/photo/2019/12/19/10/55/christmas-market-4705877_960_720.jpg', + 'https://cdn.pixabay.com/photo/2019/12/20/00/03/road-4707345_960_720.jpg', + 'https://cdn.pixabay.com/photo/2019/12/22/04/18/x-mas-4711785__340.jpg', + 'https://cdn.pixabay.com/photo/2016/11/22/07/09/spruce-1848543__340.jpg', + 'https://cdn.pixabay.com/photo/2017/12/03/18/04/christmas-balls-2995437_960_720.jpg', + 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', +]; + +void main() => runApp(MyApp()); + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) => MaterialApp( + title: 'GetWidget Example', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + debugShowCheckedModeBanner: false, + home: const MyHomePage(title: 'GetWidget Example'), + ); +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key key, this.title}) : super(key: key); + + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State + with SingleTickerProviderStateMixin { + final _scaffoldKey = new GlobalKey(); + TabController tabController; + final _ratingController = TextEditingController(); + bool check = false; + String searchData; + final TextEditingController _searchController = TextEditingController(); + int groupValue = 0; + final GFBottomSheetController _controller = GFBottomSheetController(); + + @override + void initState() { + super.initState(); + _ratingController.text = '4.5'; + tabController = TabController(length: 6, initialIndex: 3, vsync: this); + } + + @override + void dispose() { + tabController.dispose(); + super.dispose(); + } + + bool switchValue = true; + bool showToast = false; + + List list = [ + 'Flutter', + 'React', + 'Ionic', + 'Xamarin', + 'Flutter2', + 'React2', + 'Ionic2', + 'Xamarin2', + ]; + + final List imageList = [ + 'https://cdn.pixabay.com/photo/2017/12/03/18/04/christmas-balls-2995437_960_720.jpg', + 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', + 'https://cdn.pixabay.com/photo/2019/12/19/10/55/christmas-market-4705877_960_720.jpg', + 'https://cdn.pixabay.com/photo/2019/12/20/00/03/road-4707345_960_720.jpg', + 'https://cdn.pixabay.com/photo/2016/11/22/07/09/spruce-1848543__340.jpg', + 'https://cdn.pixabay.com/photo/2017/12/03/18/04/christmas-balls-2995437_960_720.jpg', + 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', + 'https://cdn.pixabay.com/photo/2017/12/03/18/04/christmas-balls-2995437_960_720.jpg', + 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', + 'https://cdn.pixabay.com/photo/2019/12/19/10/55/christmas-market-4705877_960_720.jpg', + 'https://cdn.pixabay.com/photo/2019/12/20/00/03/road-4707345_960_720.jpg', + 'https://cdn.pixabay.com/photo/2016/11/22/07/09/spruce-1848543__340.jpg', + 'https://cdn.pixabay.com/photo/2017/12/03/18/04/christmas-balls-2995437_960_720.jpg', + 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', + ]; + + void _persistentBottomSheet() { + _scaffoldKey.currentState.showBottomSheet((context) => Container( + color: Colors.redAccent, + height: 250, + child: const Center( + child: Text('Hey! guys , this is a persistent bottom sheet'), + ), + )); + } + + void _modalBottomSheetMenu() { + showModalBottomSheet( + context: context, + elevation: 10, + builder: (builder) => Container( + height: 350, + color: Colors.transparent, + child: Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10))), + child: const Center( + child: Text('This is a modal sheet'), + )), + )); + } + + @override + Widget build(BuildContext context) => Scaffold( + appBar: GFAppBar( + title: const Text('UI KIt'), + ), + body: + + ListView.builder( + itemCount: imageList.length, + itemBuilder: (context, index) { + var stuckAmount = 1.0 - index.clamp(0.0, 1.0); + return GFStickyHeader( + direction: Axis.horizontal, + header: Container( + height: 50.0, + width: 50, + color: Colors.blueGrey[700], + padding: EdgeInsets.symmetric(horizontal: 16.0), + alignment: Alignment.centerLeft, + child: Text( + 'H $index', + style: const TextStyle(color: Colors.white), + ), + ), + content: Container( + height: 100, + width: 100, + child: Image.network(imageList[index], + fit: BoxFit.cover, + width: 100, + height: 200.0), + ), + ); + } + ), + ); + +// ListView( +// children: [ +// Image.network(imageList[3], height: 200, fit: BoxFit.cover,), +// Image.network(imageList[4], height: 200, fit: BoxFit.cover,), +// GFStickyHeader( +// header: Container( +// height: 100, +// color: Colors.teal, +// padding: const EdgeInsets.symmetric(horizontal: 16), +// alignment: Alignment.centerLeft, +// child: const Text( +// 'Title', +// style: TextStyle(color: Colors.white), +// ), +// ), +// content: ListView( +// shrinkWrap: true, +// physics: const ScrollPhysics(), +// children: [ +// Image.network(imageList[0], height: 200, fit: BoxFit.fill,), +// Image.network(imageList[1], height: 200, fit: BoxFit.fill,), +// Image.network(imageList[2], height: 200, fit: BoxFit.fill,), +// Image.network(imageList[0], height: 200, fit: BoxFit.fill,), +// Image.network(imageList[1], height: 200, fit: BoxFit.fill,), +// Image.network(imageList[2], height: 200, fit: BoxFit.fill,) +// ], +// ), +// ) +// ], +// ), +// ); + +// SingleChildScrollView( +// child: Column( +// mainAxisAlignment: MainAxisAlignment.start, +// crossAxisAlignment: CrossAxisAlignment.center, +// children: [ +// Container( +// height: 10, +// ), +// Radio( +// value: 0, +// groupValue: groupValue, +// onChanged: (val) { +// setState(() { +// groupValue = val; +// }); +// }, +// ), +// Radio( +// value: 1, +// groupValue: groupValue, +// onChanged: (val) { +// setState(() { +// groupValue = val; +// }); +// }, +// ), +// ], +// ), +// ), +// bottomSheet: GFBottomSheet( +// controller: _controller, +////animationDuration: 800, +//// minContentHeight: 100, +// maxContentHeight: 300, +//// elevation: 10, +// enableExpandableContent: true, +// stickyHeaderHeight: 100, +// stickyHeader: Container( +// decoration: BoxDecoration( +// borderRadius: BorderRadius.circular(10), +// color: Colors.tealAccent), +//// height: 100, +// child: const Center( +// child: Text('Swipe me!'), +// ), +// ), +// contentBody: Container( +// color: Colors.teal, +// child: ListView( +// shrinkWrap: true, +// physics: const ScrollPhysics(), +// children: const [ +// Text('fhj'), +// Text('fhj'), +// Text('fhj'), +// Text('fhj'), +// Text('fhj'), +// Text('fhj'), +// Text('fhj'), +// Text('fhj'), +// Text('fhj'), +// Text('fhj'), +// Text('fhj'), +// Text('fhj'), +// ], +// ), +// ), +//// stickyFooter: Container( +//// color: Theme.of(context).primaryColor, +//// height: 100, +//// child: const Center( +//// child: Text('I am Footer!'), +//// ), +//// ), +//// stickyFooterHeight: 50, +// ), +// floatingActionButton: FloatingActionButton( +// child: const Icon(Icons.stars), +// onPressed: () { +// _controller.isBottomSheetOpened +// ? _controller.hideBottomSheet() +// : _controller.showBottomSheet(); +// }), +// ); +} + + + + +// void main() => runApp(MyApp()); +// +// class MyApp extends StatelessWidget { +// @override +// Widget build(BuildContext context) => MaterialApp( +// title: 'Example', +// theme: ThemeData( +// primarySwatch: Colors.blue, +// ), +// debugShowCheckedModeBanner: false, +// home: const MyHomePage(title: 'Example'), +// ); +// } +// +// class MyHomePage extends StatefulWidget { +// const MyHomePage({Key key, this.title}) : super(key: key); +// +// final String title; +// +// @override +// _MyHomePageState createState() => _MyHomePageState(); +// } +// +// class _MyHomePageState extends State +// with SingleTickerProviderStateMixin { +// +// List countryCode = [ +// {'name': 'India', 'dialCode': '91'}, +// {'name': 'xyz', 'dialCode': '92'}, +// {'name': 'abc', 'dialCode': '93'}, +// ]; +// +// @override +// Widget build(BuildContext context) => Scaffold( +// appBar: GFAppBar( +// title: const Text('UI KIt'), +// ), +// body: Scaffold( +// body: SingleChildScrollView( +// child: Column( +// mainAxisSize: MainAxisSize.min, +// mainAxisAlignment: MainAxisAlignment.start, +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Container( +// margin: EdgeInsets.only(bottom: 35), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Text( +// ' Mobile Number*', +// ), +// SizedBox( +// height: 6, +// ), +// Container( +// decoration: BoxDecoration( +// border: Border( +// bottom: BorderSide(color: Colors.white54, width: 1), +// )), +// child: Row( +// mainAxisSize: MainAxisSize.min, +// mainAxisAlignment: MainAxisAlignment.start, +// children: [ +// InkWell( +// onTap: () async { +// }, +// child: Container( +// height: 48, +// decoration: BoxDecoration( +// color: Colors.grey.withOpacity(0.5), +// // borderRadius: BorderRadius.all(Radius.circular(5.0)), +// // border: Border.all(color: Colors.green) +// ), +// child: Row( +// children: [ +// Text('+91' +// ), +// SizedBox( +// width: 6, +// ), +// Icon( +// Icons.keyboard_arrow_down, +// color: Colors.black, +// size: 16, +// ), +// SizedBox( +// width: 16, +// ), +// ], +// ), +// ), +// ), +// Expanded( +// child: TextFormField( +// keyboardType: TextInputType.number, +// validator: (String value) { +// if (value.isEmpty) { +// return 'Please Enter Mobile Number'; +// } else +// return null; +// }, +// decoration: new InputDecoration( +// hintText: 'Mobile Number', +// border: InputBorder.none, +// filled: true, +// fillColor: Colors.grey.withOpacity(0.2), +// focusedBorder: OutlineInputBorder( +// borderRadius: +// BorderRadius.all(Radius.circular(5.0)), +// borderSide: BorderSide(color: Colors.green)), +// ), +// ), +// ), +// ], +// ), +// ) +// ], +// )), +// SizedBox( +// height: 60, +// ), +// Container( +// margin: EdgeInsets.symmetric(horizontal: 16), +// child: TextFormField( +// // controller: _controller, +// decoration: InputDecoration( +// labelText: 'Search country', +// fillColor: Colors.white, +// focusedBorder: OutlineInputBorder( +// borderRadius: BorderRadius.circular(50.0), +// borderSide: BorderSide( +// color: Colors.grey, +// ), +// ), +// contentPadding: EdgeInsets.all(16), +// enabledBorder: OutlineInputBorder( +// borderRadius: BorderRadius.circular(50.0), +// borderSide: BorderSide( +// color: Colors.grey.withOpacity(0.6), +// width: 1.0, +// ), +// ), +// ), +// ), +// ), +// Container( +// height: MediaQuery.of(context).size.height - 120, +// child: ListView.builder( +// physics: ScrollPhysics(), +// shrinkWrap: true, +// itemCount: countryCode == null ? 0 : countryCode.length, +// itemBuilder: (BuildContext context, int index) { +// return Column( +// children: [ +// InkWell( +// onTap: () { +// Navigator.of(context).pop(countryCode[index]); +// }, +// child: ListTile( +// title: Text( +// countryCode[index]['name'], +// ), +// trailing: Text( +// '+${countryCode[index]['dialCode']}', +// ), +// ), +// ), +// Divider( +// height: 18, +// color: Colors.grey.withOpacity(0.5), +// ) +// ], +// ); +// })) +// ], +// ), +// ))); + diff --git a/lib/components/sticky_header/gf_sticky_header.dart b/lib/components/sticky_header/gf_sticky_header.dart new file mode 100644 index 00000000..4145f881 --- /dev/null +++ b/lib/components/sticky_header/gf_sticky_header.dart @@ -0,0 +1,1187 @@ +import 'dart:math' show min, max; + +import 'dart:math' as math; + +import 'package:flutter/foundation.dart'; + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; +import 'package:meta/meta.dart'; + +class GFStickyHeader extends MultiChildRenderObjectWidget { + GFStickyHeader({ + Key key, + this.header, this.content, this.enableHeaderOverlap = false, + @required this.direction, + this.mainAxisAlignment = MainAxisAlignment.start, + this.mainAxisSize = MainAxisSize.max, + this.crossAxisAlignment = CrossAxisAlignment.center, + this.textDirection, + this.verticalDirection = VerticalDirection.down, + this.textBaseline, + this.clipBehavior = Clip.hardEdge, + // List children = const [], + }) : assert(direction != null), + assert(mainAxisAlignment != null), + assert(mainAxisSize != null), + assert(crossAxisAlignment != null), + assert(verticalDirection != null), + assert(crossAxisAlignment != CrossAxisAlignment.baseline || textBaseline != null), + assert(clipBehavior != null), + super(key: key, children: [content, header]); + // Key key, + // @required this.header, + // @required this.content, + // this.enableHeaderOverlap = false, + // }) : super( + // key: key, + // children: [content, header], + // ); + + + /// widget can be used to define [header]. + final Widget header; + + /// widget can be used to define [content]. + final Widget content; + + /// On state true, the [header] will overlap the [content]. + /// Default value is false. + final bool enableHeaderOverlap; + + /// + /// If you know the axis in advance, then consider using a [Row] (if it's + /// horizontal) or [Column] (if it's vertical) instead of a [Flex], since that + /// will be less verbose. (For [Row] and [Column] this property is fixed to + /// the appropriate axis.) + final Axis direction; + + /// How the children should be placed along the main axis. + /// + /// For example, [MainAxisAlignment.start], the default, places the children + /// at the start (i.e., the left for a [Row] or the top for a [Column]) of the + /// main axis. + final MainAxisAlignment mainAxisAlignment; + + /// How much space should be occupied in the main axis. + /// + /// After allocating space to children, there might be some remaining free + /// space. This value controls whether to maximize or minimize the amount of + /// free space, subject to the incoming layout constraints. + /// + /// If some children have a non-zero flex factors (and none have a fit of + /// [FlexFit.loose]), they will expand to consume all the available space and + /// there will be no remaining free space to maximize or minimize, making this + /// value irrelevant to the final layout. + final MainAxisSize mainAxisSize; + + /// How the children should be placed along the cross axis. + /// + /// For example, [CrossAxisAlignment.center], the default, centers the + /// children in the cross axis (e.g., horizontally for a [Column]). + final CrossAxisAlignment crossAxisAlignment; + + /// Determines the order to lay children out horizontally and how to interpret + /// `start` and `end` in the horizontal direction. + /// + /// Defaults to the ambient [Directionality]. + /// + /// If the [direction] is [Axis.horizontal], this controls the order in which + /// the children are positioned (left-to-right or right-to-left), and the + /// meaning of the [mainAxisAlignment] property's [MainAxisAlignment.start] and + /// [MainAxisAlignment.end] values. + /// + /// If the [direction] is [Axis.horizontal], and either the + /// [mainAxisAlignment] is either [MainAxisAlignment.start] or + /// [MainAxisAlignment.end], or there's more than one child, then the + /// [textDirection] (or the ambient [Directionality]) must not be null. + /// + /// If the [direction] is [Axis.vertical], this controls the meaning of the + /// [crossAxisAlignment] property's [CrossAxisAlignment.start] and + /// [CrossAxisAlignment.end] values. + /// + /// If the [direction] is [Axis.vertical], and the [crossAxisAlignment] is + /// either [CrossAxisAlignment.start] or [CrossAxisAlignment.end], then the + /// [textDirection] (or the ambient [Directionality]) must not be null. + final TextDirection textDirection; + + /// Determines the order to lay children out vertically and how to interpret + /// `start` and `end` in the vertical direction. + /// + /// Defaults to [VerticalDirection.down]. + /// + /// If the [direction] is [Axis.vertical], this controls which order children + /// are painted in (down or up), the meaning of the [mainAxisAlignment] + /// property's [MainAxisAlignment.start] and [MainAxisAlignment.end] values. + /// + /// If the [direction] is [Axis.vertical], and either the [mainAxisAlignment] + /// is either [MainAxisAlignment.start] or [MainAxisAlignment.end], or there's + /// more than one child, then the [verticalDirection] must not be null. + /// + /// If the [direction] is [Axis.horizontal], this controls the meaning of the + /// [crossAxisAlignment] property's [CrossAxisAlignment.start] and + /// [CrossAxisAlignment.end] values. + /// + /// If the [direction] is [Axis.horizontal], and the [crossAxisAlignment] is + /// either [CrossAxisAlignment.start] or [CrossAxisAlignment.end], then the + /// [verticalDirection] must not be null. + final VerticalDirection verticalDirection; + + /// If aligning items according to their baseline, which baseline to use. + final TextBaseline textBaseline; + + // TODO(liyuqian): defaults to [Clip.none] once Google references are updated. + /// {@macro flutter.widgets.Clip} + /// + /// Defaults to [Clip.hardEdge]. + final Clip clipBehavior; + + bool get _needTextDirection { + assert(direction != null); + switch (direction) { + case Axis.horizontal: + return true; // because it affects the layout order. + case Axis.vertical: + assert(crossAxisAlignment != null); + return crossAxisAlignment == CrossAxisAlignment.start + || crossAxisAlignment == CrossAxisAlignment.end; + } + return null; + } + + /// The value to pass to [RenderFlex.textDirection]. + /// + /// This value is derived from the [textDirection] property and the ambient + /// [Directionality]. The value is null if there is no need to specify the + /// text direction. In practice there's always a need to specify the direction + /// except for vertical flexes (e.g. [Column]s) whose [crossAxisAlignment] is + /// not dependent on the text direction (not `start` or `end`). In particular, + /// a [Row] always needs a text direction because the text direction controls + /// its layout order. (For [Column]s, the layout order is controlled by + /// [verticalDirection], which is always specified as it does not depend on an + /// inherited widget and defaults to [VerticalDirection.down].) + /// + /// This method exists so that subclasses of [Flex] that create their own + /// render objects that are derived from [RenderFlex] can do so and still use + /// the logic for providing a text direction only when it is necessary. + @protected + TextDirection getEffectiveTextDirection(BuildContext context) { + return textDirection ?? (_needTextDirection ? Directionality.of(context) : null); + } + + + @override + RenderGFStickyHeader createRenderObject(BuildContext context) { + final scrollable = Scrollable.of(context); + assert(scrollable != null); + return RenderGFStickyHeader( + direction: direction, + mainAxisAlignment: mainAxisAlignment, + mainAxisSize: mainAxisSize, + crossAxisAlignment: crossAxisAlignment, + textDirection: getEffectiveTextDirection(context), + verticalDirection: verticalDirection, + textBaseline: textBaseline, + clipBehavior: clipBehavior, + scrollable: scrollable, + ); + } +} + +bool _startIsTopLeft(Axis direction, TextDirection textDirection, VerticalDirection verticalDirection) { + assert(direction != null); + // If the relevant value of textDirection or verticalDirection is null, this returns null too. + switch (direction) { + case Axis.horizontal: + switch (textDirection) { + case TextDirection.ltr: + return true; + case TextDirection.rtl: + return false; + } + break; + case Axis.vertical: + switch (verticalDirection) { + case VerticalDirection.down: + return true; + case VerticalDirection.up: + return false; + } + break; + } + return null; +} + + +typedef _ChildSizingFunction = double Function(RenderBox child, double extent); + +class RenderGFStickyHeader extends RenderBox with ContainerRenderObjectMixin, + RenderBoxContainerDefaultsMixin, + DebugOverflowIndicatorMixin { + /// Creates a flex render object. + /// + /// By default, the flex layout is horizontal and children are aligned to the + /// start of the main axis and the center of the cross axis. + RenderGFStickyHeader({ + List children, + Axis direction = Axis.horizontal, + MainAxisSize mainAxisSize = MainAxisSize.max, + MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, + CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, + TextDirection textDirection, + VerticalDirection verticalDirection = VerticalDirection.down, + TextBaseline textBaseline, + Clip clipBehavior = Clip.none, + bool enableHeaderOverlap = false, + ScrollableState scrollable, + }) : assert(direction != null), + assert(mainAxisAlignment != null), + assert(mainAxisSize != null), + assert(crossAxisAlignment != null), + assert(clipBehavior != null), + assert(scrollable != null), + _scrollable = scrollable, + _direction = direction, + _mainAxisAlignment = mainAxisAlignment, + _mainAxisSize = mainAxisSize, + _crossAxisAlignment = crossAxisAlignment, + _textDirection = textDirection, + _verticalDirection = verticalDirection, + _textBaseline = textBaseline, + _enableHeaderOverlap = enableHeaderOverlap { + addAll(children); + } + // _clipBehavior = clipBehavior { + // addAll(children); + // } + + + + final ScrollableState _scrollable; + final bool _enableHeaderOverlap; + + /// The direction to use as the main axis. + Axis get direction => _direction; + Axis _direction; + set direction(Axis value) { + assert(value != null); + if (_direction != value) { + _direction = value; + markNeedsLayout(); + } + } + + /// How the children should be placed along the main axis. + /// + /// If the [direction] is [Axis.horizontal], and the [mainAxisAlignment] is + /// either [MainAxisAlignment.start] or [MainAxisAlignment.end], then the + /// [textDirection] must not be null. + /// + /// If the [direction] is [Axis.vertical], and the [mainAxisAlignment] is + /// either [MainAxisAlignment.start] or [MainAxisAlignment.end], then the + /// [verticalDirection] must not be null. + MainAxisAlignment get mainAxisAlignment => _mainAxisAlignment; + MainAxisAlignment _mainAxisAlignment; + set mainAxisAlignment(MainAxisAlignment value) { + assert(value != null); + if (_mainAxisAlignment != value) { + _mainAxisAlignment = value; + markNeedsLayout(); + } + } + + /// How much space should be occupied in the main axis. + /// + /// After allocating space to children, there might be some remaining free + /// space. This value controls whether to maximize or minimize the amount of + /// free space, subject to the incoming layout constraints. + /// + /// If some children have a non-zero flex factors (and none have a fit of + /// [FlexFit.loose]), they will expand to consume all the available space and + /// there will be no remaining free space to maximize or minimize, making this + /// value irrelevant to the final layout. + MainAxisSize get mainAxisSize => _mainAxisSize; + MainAxisSize _mainAxisSize; + set mainAxisSize(MainAxisSize value) { + assert(value != null); + if (_mainAxisSize != value) { + _mainAxisSize = value; + markNeedsLayout(); + } + } + + /// How the children should be placed along the cross axis. + /// + /// If the [direction] is [Axis.horizontal], and the [crossAxisAlignment] is + /// either [CrossAxisAlignment.start] or [CrossAxisAlignment.end], then the + /// [verticalDirection] must not be null. + /// + /// If the [direction] is [Axis.vertical], and the [crossAxisAlignment] is + /// either [CrossAxisAlignment.start] or [CrossAxisAlignment.end], then the + /// [textDirection] must not be null. + CrossAxisAlignment get crossAxisAlignment => _crossAxisAlignment; + CrossAxisAlignment _crossAxisAlignment; + set crossAxisAlignment(CrossAxisAlignment value) { + assert(value != null); + if (_crossAxisAlignment != value) { + _crossAxisAlignment = value; + markNeedsLayout(); + } + } + + /// Determines the order to lay children out horizontally and how to interpret + /// `start` and `end` in the horizontal direction. + /// + /// If the [direction] is [Axis.horizontal], this controls the order in which + /// children are positioned (left-to-right or right-to-left), and the meaning + /// of the [mainAxisAlignment] property's [MainAxisAlignment.start] and + /// [MainAxisAlignment.end] values. + /// + /// If the [direction] is [Axis.horizontal], and either the + /// [mainAxisAlignment] is either [MainAxisAlignment.start] or + /// [MainAxisAlignment.end], or there's more than one child, then the + /// [textDirection] must not be null. + /// + /// If the [direction] is [Axis.vertical], this controls the meaning of the + /// [crossAxisAlignment] property's [CrossAxisAlignment.start] and + /// [CrossAxisAlignment.end] values. + /// + /// If the [direction] is [Axis.vertical], and the [crossAxisAlignment] is + /// either [CrossAxisAlignment.start] or [CrossAxisAlignment.end], then the + /// [textDirection] must not be null. + TextDirection get textDirection => _textDirection; + TextDirection _textDirection; + set textDirection(TextDirection value) { + if (_textDirection != value) { + _textDirection = value; + markNeedsLayout(); + } + } + + /// Determines the order to lay children out vertically and how to interpret + /// `start` and `end` in the vertical direction. + /// + /// If the [direction] is [Axis.vertical], this controls which order children + /// are painted in (down or up), the meaning of the [mainAxisAlignment] + /// property's [MainAxisAlignment.start] and [MainAxisAlignment.end] values. + /// + /// If the [direction] is [Axis.vertical], and either the [mainAxisAlignment] + /// is either [MainAxisAlignment.start] or [MainAxisAlignment.end], or there's + /// more than one child, then the [verticalDirection] must not be null. + /// + /// If the [direction] is [Axis.horizontal], this controls the meaning of the + /// [crossAxisAlignment] property's [CrossAxisAlignment.start] and + /// [CrossAxisAlignment.end] values. + /// + /// If the [direction] is [Axis.horizontal], and the [crossAxisAlignment] is + /// either [CrossAxisAlignment.start] or [CrossAxisAlignment.end], then the + /// [verticalDirection] must not be null. + VerticalDirection get verticalDirection => _verticalDirection; + VerticalDirection _verticalDirection; + set verticalDirection(VerticalDirection value) { + if (_verticalDirection != value) { + _verticalDirection = value; + markNeedsLayout(); + } + } + + /// If aligning items according to their baseline, which baseline to use. + /// + /// Must not be null if [crossAxisAlignment] is [CrossAxisAlignment.baseline]. + TextBaseline get textBaseline => _textBaseline; + TextBaseline _textBaseline; + set textBaseline(TextBaseline value) { + assert(_crossAxisAlignment != CrossAxisAlignment.baseline || value != null); + if (_textBaseline != value) { + _textBaseline = value; + markNeedsLayout(); + } + } + + // bool get _debugHasNecessaryDirections { + // assert(direction != null); + // assert(crossAxisAlignment != null); + // if (firstChild != null && lastChild != firstChild) { + // // i.e. there's more than one child + // switch (direction) { + // case Axis.horizontal: + // assert(textDirection != null, 'Horizontal $runtimeType with multiple children has a null textDirection, so the layout order is undefined.'); + // break; + // case Axis.vertical: + // assert(verticalDirection != null, 'Vertical $runtimeType with multiple children has a null verticalDirection, so the layout order is undefined.'); + // break; + // } + // } + // if (mainAxisAlignment == MainAxisAlignment.start || + // mainAxisAlignment == MainAxisAlignment.end) { + // switch (direction) { + // case Axis.horizontal: + // assert(textDirection != null, 'Horizontal $runtimeType with $mainAxisAlignment has a null textDirection, so the alignment cannot be resolved.'); + // break; + // case Axis.vertical: + // assert(verticalDirection != null, 'Vertical $runtimeType with $mainAxisAlignment has a null verticalDirection, so the alignment cannot be resolved.'); + // break; + // } + // } + // if (crossAxisAlignment == CrossAxisAlignment.start || + // crossAxisAlignment == CrossAxisAlignment.end) { + // switch (direction) { + // case Axis.horizontal: + // assert(verticalDirection != null, 'Horizontal $runtimeType with $crossAxisAlignment has a null verticalDirection, so the alignment cannot be resolved.'); + // break; + // case Axis.vertical: + // assert(textDirection != null, 'Vertical $runtimeType with $crossAxisAlignment has a null textDirection, so the alignment cannot be resolved.'); + // break; + // } + // } + // return true; + // } + + // Set during layout if overflow occurred on the main axis. + double _overflow; + // Check whether any meaningful overflow is present. Values below an epsilon + // are treated as not overflowing. + // bool get _hasOverflow => _overflow > precisionErrorTolerance; + + /// {@macro flutter.widgets.Clip} + /// + /// Defaults to [Clip.none], and must not be null. + Clip get clipBehavior => _clipBehavior; + Clip _clipBehavior = Clip.none; + set clipBehavior(Clip value) { + assert(value != null); + if (value != _clipBehavior) { + _clipBehavior = value; + markNeedsPaint(); + markNeedsSemanticsUpdate(); + } + } + + @override + void setupParentData(RenderBox child) { + if (child.parentData is! FlexParentData) { + child.parentData = FlexParentData(); + } + // super.setupParentData(child); + // child.parentData = MultiChildLayoutParentData(); + } + + // double _getIntrinsicSize({ + // Axis sizingDirection, + // double extent, // the extent in the direction that isn't the sizing direction + // _ChildSizingFunction childSize, // a method to find the size in the sizing direction + // }) { + // if (_direction == sizingDirection) { + // // INTRINSIC MAIN SIZE + // // Intrinsic main size is the smallest size the flex container can take + // // while maintaining the min/max-content contributions of its flex items. + // double totalFlex = 0.0; + // double inflexibleSpace = 0.0; + // double maxFlexFractionSoFar = 0.0; + // RenderBox child = firstChild; + // while (child != null) { + // final int flex = _getFlex(child); + // totalFlex += flex; + // if (flex > 0) { + // final double flexFraction = childSize(child, extent) / _getFlex(child); + // maxFlexFractionSoFar = math.max(maxFlexFractionSoFar, flexFraction); + // } else { + // inflexibleSpace += childSize(child, extent); + // } + // final FlexParentData childParentData = child.parentData as FlexParentData; + // child = childParentData.nextSibling; + // } + // return maxFlexFractionSoFar * totalFlex + inflexibleSpace; + // } else { + // // INTRINSIC CROSS SIZE + // // Intrinsic cross size is the max of the intrinsic cross sizes of the + // // children, after the flexible children are fit into the available space, + // // with the children sized using their max intrinsic dimensions. + // + // // Get inflexible space using the max intrinsic dimensions of fixed children in the main direction. + // final double availableMainSpace = extent; + // int totalFlex = 0; + // double inflexibleSpace = 0.0; + // double maxCrossSize = 0.0; + // RenderBox child = firstChild; + // while (child != null) { + // final int flex = _getFlex(child); + // totalFlex += flex; + // double mainSize; + // double crossSize; + // if (flex == 0) { + // switch (_direction) { + // case Axis.horizontal: + // mainSize = child.getMaxIntrinsicWidth(double.infinity); + // crossSize = childSize(child, mainSize); + // break; + // case Axis.vertical: + // mainSize = child.getMaxIntrinsicHeight(double.infinity); + // crossSize = childSize(child, mainSize); + // break; + // } + // inflexibleSpace += mainSize; + // maxCrossSize = math.max(maxCrossSize, crossSize); + // } + // final FlexParentData childParentData = child.parentData as FlexParentData; + // child = childParentData.nextSibling; + // } + // + // // Determine the spacePerFlex by allocating the remaining available space. + // // When you're overconstrained spacePerFlex can be negative. + // final double spacePerFlex = math.max(0.0, + // (availableMainSpace - inflexibleSpace) / totalFlex); + // + // // Size remaining (flexible) items, find the maximum cross size. + // child = firstChild; + // while (child != null) { + // final int flex = _getFlex(child); + // if (flex > 0) + // maxCrossSize = math.max(maxCrossSize, childSize(child, spacePerFlex * flex)); + // final FlexParentData childParentData = child.parentData as FlexParentData; + // child = childParentData.nextSibling; + // } + // + // return maxCrossSize; + // } + // } + + // @override + // double computeMinIntrinsicWidth(double height) { + // return _getIntrinsicSize( + // sizingDirection: Axis.horizontal, + // extent: height, + // childSize: (RenderBox child, double extent) => child.getMinIntrinsicWidth(extent), + // ); + // } + // + // @override + // double computeMaxIntrinsicWidth(double height) { + // return _getIntrinsicSize( + // sizingDirection: Axis.horizontal, + // extent: height, + // childSize: (RenderBox child, double extent) => child.getMaxIntrinsicWidth(extent), + // ); + // } + // + // @override + // double computeMinIntrinsicHeight(double width) { + // return _getIntrinsicSize( + // sizingDirection: Axis.vertical, + // extent: width, + // childSize: (RenderBox child, double extent) => child.getMinIntrinsicHeight(extent), + // ); + // } + // + // @override + // double computeMaxIntrinsicHeight(double width) { + // return _getIntrinsicSize( + // sizingDirection: Axis.vertical, + // extent: width, + // childSize: (RenderBox child, double extent) => child.getMaxIntrinsicHeight(extent), + // ); + // } + + @override + double computeDistanceToActualBaseline(TextBaseline baseline) { + if (_direction == Axis.horizontal) + return defaultComputeDistanceToHighestActualBaseline(baseline); + return defaultComputeDistanceToFirstActualBaseline(baseline); + } + + int _getFlex(RenderBox child) { + final FlexParentData childParentData = child.parentData as FlexParentData; + return childParentData.flex ?? 0; + } + + FlexFit _getFit(RenderBox child) { + final FlexParentData childParentData = child.parentData as FlexParentData; + return childParentData.fit ?? FlexFit.tight; + } + + double _getCrossSize(RenderBox child) { + switch (_direction) { + case Axis.horizontal: + return child.size.height; + case Axis.vertical: + return child.size.width; + } + return null; + } + + double _getMainSize(RenderBox child) { + switch (_direction) { + case Axis.horizontal: + return child.size.width; + case Axis.vertical: + return child.size.height; + } + return null; + } + + @override + void attach(PipelineOwner owner) { + super.attach(owner); + _scrollable.position.addListener(markNeedsLayout); + } + + @override + void detach() { + _scrollable.position.removeListener(markNeedsLayout); + super.detach(); + } + + RenderBox get _headerTile => lastChild; + + RenderBox get _contentBody => firstChild; + + double getHeaderTileStuckOffset() { + final scrollableContent = _scrollable.context.findRenderObject(); + if (scrollableContent.attached) { + try { + return localToGlobal(Offset.zero, ancestor: scrollableContent).dy; + // ignore: avoid_catches_without_on_clauses + } catch (e) { + print(e); + } + } + return 0; + } + + + @override + void performLayout() { + // assert(_debugHasNecessaryDirections); + // final BoxConstraints constraints = this.constraints; + + assert(childCount == 2); + _headerTile.layout(constraints.loosen(), parentUsesSize: true); + _contentBody.layout(constraints.loosen(), parentUsesSize: true); + + final headerTileHeight = _headerTile.size.height; + final contentBodyHeight = _contentBody.size.height; + + final height = max( + constraints.minHeight, + _enableHeaderOverlap + ? contentBodyHeight + : headerTileHeight + contentBodyHeight); + final width = max(constraints.minWidth, contentBodyHeight); + + size = Size( + constraints.constrainWidth(width), constraints.constrainHeight(height)); + + final double headerTileOffset = getHeaderTileStuckOffset(); + + final FlexParentData contentBodyParentData = + _contentBody.parentData; + contentBodyParentData.offset = + Offset(0, _enableHeaderOverlap ? 0.0 : headerTileHeight); + + + final FlexParentData headerTileParentData = + _headerTile.parentData; + headerTileParentData.offset = + Offset(0, max(0, min(-headerTileOffset, height - headerTileHeight))); + + // Determine used flex factor, size inflexible items, calculate free space. + int totalFlex = 0; + int totalChildren = 0; + assert(constraints != null); + final double maxMainSize = _direction == Axis.horizontal ? constraints.maxWidth : constraints.maxHeight; + final bool canFlex = maxMainSize < double.infinity; + + double crossSize = 0.0; + double allocatedSize = 0.0; // Sum of the sizes of the non-flexible children. + RenderBox child = firstChild; + RenderBox lastFlexChild; + while (child != null) { + final FlexParentData childParentData = child.parentData as FlexParentData; + totalChildren++; + final int flex = _getFlex(child); + if (flex > 0) { + assert(() { + final String identity = _direction == Axis.horizontal ? 'row' : 'column'; + final String axis = _direction == Axis.horizontal ? 'horizontal' : 'vertical'; + final String dimension = _direction == Axis.horizontal ? 'width' : 'height'; + DiagnosticsNode error, message; + final List addendum = []; + if (!canFlex && (mainAxisSize == MainAxisSize.max || _getFit(child) == FlexFit.tight)) { + error = ErrorSummary('RenderFlex children have non-zero flex but incoming $dimension constraints are unbounded.'); + message = ErrorDescription( + 'When a $identity is in a parent that does not provide a finite $dimension constraint, for example ' + 'if it is in a $axis scrollable, it will try to shrink-wrap its children along the $axis ' + 'axis. Setting a flex on a child (e.g. using Expanded) indicates that the child is to ' + 'expand to fill the remaining space in the $axis direction.' + ); + RenderBox node = this; + switch (_direction) { + case Axis.horizontal: + while (!node.constraints.hasBoundedWidth && node.parent is RenderBox) + node = node.parent as RenderBox; + if (!node.constraints.hasBoundedWidth) + node = null; + break; + case Axis.vertical: + while (!node.constraints.hasBoundedHeight && node.parent is RenderBox) + node = node.parent as RenderBox; + if (!node.constraints.hasBoundedHeight) + node = null; + break; + } + if (node != null) { + addendum.add(node.describeForError('The nearest ancestor providing an unbounded width constraint is')); + } + addendum.add(ErrorHint('See also: https://flutter.dev/layout/')); + } else { + return true; + } + throw FlutterError.fromParts([ + error, + message, + ErrorDescription( + 'These two directives are mutually exclusive. If a parent is to shrink-wrap its child, the child ' + 'cannot simultaneously expand to fit its parent.' + ), + ErrorHint( + 'Consider setting mainAxisSize to MainAxisSize.min and using FlexFit.loose fits for the flexible ' + 'children (using Flexible rather than Expanded). This will allow the flexible children ' + 'to size themselves to less than the infinite remaining space they would otherwise be ' + 'forced to take, and then will cause the RenderFlex to shrink-wrap the children ' + 'rather than expanding to fit the maximum constraints provided by the parent.' + ), + ErrorDescription( + 'If this message did not help you determine the problem, consider using debugDumpRenderTree():\n' + ' https://flutter.dev/debugging/#rendering-layer\n' + ' http://api.flutter.dev/flutter/rendering/debugDumpRenderTree.html' + ), + describeForError('The affected RenderFlex is', style: DiagnosticsTreeStyle.errorProperty), + DiagnosticsProperty('The creator information is set to', debugCreator, style: DiagnosticsTreeStyle.errorProperty), + ...addendum, + ErrorDescription( + "If none of the above helps enough to fix this problem, please don't hesitate to file a bug:\n" + ' https://github.com/flutter/flutter/issues/new?template=BUG.md' + ), + ]); + }()); + totalFlex += childParentData.flex; + lastFlexChild = child; + } else { + BoxConstraints innerConstraints; + if (crossAxisAlignment == CrossAxisAlignment.stretch) { + switch (_direction) { + case Axis.horizontal: + innerConstraints = BoxConstraints(minHeight: constraints.maxHeight, + maxHeight: constraints.maxHeight); + break; + case Axis.vertical: + innerConstraints = BoxConstraints(minWidth: constraints.maxWidth, + maxWidth: constraints.maxWidth); + break; + } + } else { + switch (_direction) { + case Axis.horizontal: + innerConstraints = BoxConstraints(maxHeight: constraints.maxHeight); + break; + case Axis.vertical: + innerConstraints = BoxConstraints(maxWidth: constraints.maxWidth); + break; + } + } + child.layout(innerConstraints, parentUsesSize: true); + allocatedSize += _getMainSize(child); + crossSize = math.max(crossSize, _getCrossSize(child)); + } + assert(child.parentData == childParentData); + child = childParentData.nextSibling; + } + + // Distribute free space to flexible children, and determine baseline. + final double freeSpace = math.max(0.0, (canFlex ? maxMainSize : 0.0) - allocatedSize); + double allocatedFlexSpace = 0.0; + double maxBaselineDistance = 0.0; + if (totalFlex > 0 || crossAxisAlignment == CrossAxisAlignment.baseline) { + final double spacePerFlex = canFlex && totalFlex > 0 ? (freeSpace / totalFlex) : double.nan; + child = firstChild; + double maxSizeAboveBaseline = 0; + double maxSizeBelowBaseline = 0; + while (child != null) { + final int flex = _getFlex(child); + if (flex > 0) { + final double maxChildExtent = canFlex ? (child == lastFlexChild ? (freeSpace - allocatedFlexSpace) : spacePerFlex * flex) : double.infinity; + double minChildExtent; + switch (_getFit(child)) { + case FlexFit.tight: + assert(maxChildExtent < double.infinity); + minChildExtent = maxChildExtent; + break; + case FlexFit.loose: + minChildExtent = 0.0; + break; + } + assert(minChildExtent != null); + BoxConstraints innerConstraints; + if (crossAxisAlignment == CrossAxisAlignment.stretch) { + switch (_direction) { + case Axis.horizontal: + innerConstraints = BoxConstraints(minWidth: minChildExtent, + maxWidth: maxChildExtent, + minHeight: constraints.maxHeight, + maxHeight: constraints.maxHeight); + break; + case Axis.vertical: + innerConstraints = BoxConstraints(minWidth: constraints.maxWidth, + maxWidth: constraints.maxWidth, + minHeight: minChildExtent, + maxHeight: maxChildExtent); + break; + } + } else { + switch (_direction) { + case Axis.horizontal: + innerConstraints = BoxConstraints(minWidth: minChildExtent, + maxWidth: maxChildExtent, + maxHeight: constraints.maxHeight); + break; + case Axis.vertical: + innerConstraints = BoxConstraints(maxWidth: constraints.maxWidth, + minHeight: minChildExtent, + maxHeight: maxChildExtent); + break; + } + } + child.layout(innerConstraints, parentUsesSize: true); + final double childSize = _getMainSize(child); + assert(childSize <= maxChildExtent); + allocatedSize += childSize; + allocatedFlexSpace += maxChildExtent; + crossSize = math.max(crossSize, _getCrossSize(child)); + } + if (crossAxisAlignment == CrossAxisAlignment.baseline) { + assert(() { + if (textBaseline == null) + throw FlutterError('To use FlexAlignItems.baseline, you must also specify which baseline to use using the "baseline" argument.'); + return true; + }()); + final double distance = child.getDistanceToBaseline(textBaseline, onlyReal: true); + if (distance != null) { + maxBaselineDistance = math.max(maxBaselineDistance, distance); + maxSizeAboveBaseline = math.max( + distance, + maxSizeAboveBaseline, + ); + maxSizeBelowBaseline = math.max( + child.size.height - distance, + maxSizeBelowBaseline, + ); + crossSize = maxSizeAboveBaseline + maxSizeBelowBaseline; + } + } + final FlexParentData childParentData = child.parentData as FlexParentData; + child = childParentData.nextSibling; + } + } + + // Align items along the main axis. + final double idealSize = canFlex && mainAxisSize == MainAxisSize.max ? maxMainSize : allocatedSize; + double actualSize; + double actualSizeDelta; + switch (_direction) { + case Axis.horizontal: + size = constraints.constrain(Size(idealSize, crossSize)); + actualSize = size.width; + crossSize = size.height; + break; + case Axis.vertical: + size = constraints.constrain(Size(crossSize, idealSize)); + actualSize = size.height; + crossSize = size.width; + break; + } + actualSizeDelta = actualSize - allocatedSize; + _overflow = math.max(0.0, -actualSizeDelta); + final double remainingSpace = math.max(0.0, actualSizeDelta); + double leadingSpace; + double betweenSpace; + // flipMainAxis is used to decide whether to lay out left-to-right/top-to-bottom (false), or + // right-to-left/bottom-to-top (true). The _startIsTopLeft will return null if there's only + // one child and the relevant direction is null, in which case we arbitrarily decide not to + // flip, but that doesn't have any detectable effect. + final bool flipMainAxis = !(_startIsTopLeft(direction, textDirection, verticalDirection) ?? true); + switch (_mainAxisAlignment) { + case MainAxisAlignment.start: + leadingSpace = 0.0; + betweenSpace = 0.0; + break; + case MainAxisAlignment.end: + leadingSpace = remainingSpace; + betweenSpace = 0.0; + break; + case MainAxisAlignment.center: + leadingSpace = remainingSpace / 2.0; + betweenSpace = 0.0; + break; + case MainAxisAlignment.spaceBetween: + leadingSpace = 0.0; + betweenSpace = totalChildren > 1 ? remainingSpace / (totalChildren - 1) : 0.0; + break; + case MainAxisAlignment.spaceAround: + betweenSpace = totalChildren > 0 ? remainingSpace / totalChildren : 0.0; + leadingSpace = betweenSpace / 2.0; + break; + case MainAxisAlignment.spaceEvenly: + betweenSpace = totalChildren > 0 ? remainingSpace / (totalChildren + 1) : 0.0; + leadingSpace = betweenSpace; + break; + } + + // Position elements + double childMainPosition = flipMainAxis ? actualSize - leadingSpace : leadingSpace; + child = firstChild; + while (child != null) { + final FlexParentData childParentData = child.parentData as FlexParentData; + double childCrossPosition; + switch (_crossAxisAlignment) { + case CrossAxisAlignment.start: + case CrossAxisAlignment.end: + childCrossPosition = _startIsTopLeft(flipAxis(direction), textDirection, verticalDirection) + == (_crossAxisAlignment == CrossAxisAlignment.start) + ? 0.0 + : crossSize - _getCrossSize(child); + break; + case CrossAxisAlignment.center: + childCrossPosition = crossSize / 2.0 - _getCrossSize(child) / 2.0; + break; + case CrossAxisAlignment.stretch: + childCrossPosition = 0.0; + break; + case CrossAxisAlignment.baseline: + childCrossPosition = 0.0; + if (_direction == Axis.horizontal) { + assert(textBaseline != null); + final double distance = child.getDistanceToBaseline(textBaseline, onlyReal: true); + if (distance != null) + childCrossPosition = maxBaselineDistance - distance; + } + break; + } + + if (flipMainAxis) + childMainPosition -= _getMainSize(child); + switch (_direction) { + case Axis.horizontal: + final FlexParentData headerTileParentData = + _headerTile.parentData; + headerTileParentData.offset = Offset(childMainPosition, childCrossPosition); + // headerTileParentData.offset = + // Offset(max(0, min(-headerTileOffset, height - headerTileHeight)), 0); + break; + case Axis.vertical: + final FlexParentData headerTileParentData = + _headerTile.parentData; + headerTileParentData.offset = + Offset(0, max(0, min(-headerTileOffset, height - headerTileHeight))); + // childParentData.offset = Offset(0, max(0, min(-headerTileOffset, height - headerTileHeight))); + break; + } + if (flipMainAxis) { + childMainPosition -= betweenSpace; + } else { + childMainPosition += _getMainSize(child) + betweenSpace; + } + child = childParentData.nextSibling; + } + } + + @override + bool hitTestChildren(BoxHitTestResult result, { Offset position }) { + return defaultHitTestChildren(result, position: position); + } + + @override + void paint(PaintingContext context, Offset offset) { + defaultPaint(context, offset); + + // if (!_hasOverflow) { + // defaultPaint(context, offset); + // return; + // } + + // There's no point in drawing the children if we're empty. + // if (size.isEmpty) + // return; + // + // if (clipBehavior == Clip.none) { + // defaultPaint(context, offset); + // } else { + // // We have overflow and the clipBehavior isn't none. Clip it. + // context.pushClipRect(needsCompositing, offset, Offset.zero & size, defaultPaint, clipBehavior: clipBehavior); + // } + // + // assert(() { + // // Only set this if it's null to save work. It gets reset to null if the + // // _direction changes. + // final List debugOverflowHints = [ + // ErrorDescription( + // 'The overflowing $runtimeType has an orientation of $_direction.' + // ), + // ErrorDescription( + // 'The edge of the $runtimeType that is overflowing has been marked ' + // 'in the rendering with a yellow and black striped pattern. This is ' + // 'usually caused by the contents being too big for the $runtimeType.' + // ), + // ErrorHint( + // 'Consider applying a flex factor (e.g. using an Expanded widget) to ' + // 'force the children of the $runtimeType to fit within the available ' + // 'space instead of being sized to their natural size.' + // ), + // ErrorHint( + // 'This is considered an error condition because it indicates that there ' + // 'is content that cannot be seen. If the content is legitimately bigger ' + // 'than the available space, consider clipping it with a ClipRect widget ' + // 'before putting it in the flex, or using a scrollable container rather ' + // 'than a Flex, like a ListView.' + // ), + // ]; + // + // // Simulate a child rect that overflows by the right amount. This child + // // rect is never used for drawing, just for determining the overflow + // // location and amount. + // Rect overflowChildRect; + // switch (_direction) { + // case Axis.horizontal: + // overflowChildRect = Rect.fromLTWH(0.0, 0.0, size.width + _overflow, 0.0); + // break; + // case Axis.vertical: + // overflowChildRect = Rect.fromLTWH(0.0, 0.0, 0.0, size.height + _overflow); + // break; + // } + // paintOverflowIndicator(context, offset, Offset.zero & size, overflowChildRect, overflowHints: debugOverflowHints); + // return true; + // }()); + } + + // @override + // Rect describeApproximatePaintClip(RenderObject child) => _hasOverflow ? Offset.zero & size : null; + // + // @override + // String toStringShort() { + // String header = super.toStringShort(); + // if (_overflow is double && _hasOverflow) + // header += ' OVERFLOWING'; + // return header; + // } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(EnumProperty('direction', direction)); + properties.add(EnumProperty('mainAxisAlignment', mainAxisAlignment)); + properties.add(EnumProperty('mainAxisSize', mainAxisSize)); + properties.add(EnumProperty('crossAxisAlignment', crossAxisAlignment)); + properties.add(EnumProperty('textDirection', textDirection, defaultValue: null)); + properties.add(EnumProperty('verticalDirection', verticalDirection, defaultValue: null)); + properties.add(EnumProperty('textBaseline', textBaseline, defaultValue: null)); + } +} + +// class RenderGFStickyHeader extends RenderBox +// with +// ContainerRenderObjectMixin, +// RenderBoxContainerDefaultsMixin { +// RenderGFStickyHeader({ +// @required ScrollableState scrollable, +// bool enableHeaderOverlap = false, +// RenderBox header, +// RenderBox content, +// }) : assert(scrollable != null), +// _scrollable = scrollable, +// _enableHeaderOverlap = enableHeaderOverlap { +// if (content != null) { +// add(content); +// } +// if (header != null) { +// add(header); +// } +// } +// +// final ScrollableState _scrollable; +// final bool _enableHeaderOverlap; +// +// @override +// void attach(PipelineOwner owner) { +// super.attach(owner); +// _scrollable.position.addListener(markNeedsLayout); +// } +// +// @override +// void detach() { +// _scrollable.position.removeListener(markNeedsLayout); +// super.detach(); +// } +// +// RenderBox get _headerTile => lastChild; +// +// RenderBox get _contentBody => firstChild; +// +// @override +// void performLayout() { +// +// assert(childCount == 2); +// _headerTile.layout(constraints.loosen(), parentUsesSize: true); +// _contentBody.layout(constraints.loosen(), parentUsesSize: true); +// +// final headerTileHeight = _headerTile.size.height; +// final contentBodyHeight = _contentBody.size.height; +// +// final height = max( +// constraints.minHeight, +// _enableHeaderOverlap +// ? contentBodyHeight +// : headerTileHeight + contentBodyHeight); +// final width = max(constraints.minWidth, contentBodyHeight); +// +// size = Size( +// constraints.constrainWidth(width), constraints.constrainHeight(height)); +// +// final double headerTileOffset = getHeaderTileStuckOffset(); +// +// final MultiChildLayoutParentData contentBodyParentData = +// _contentBody.parentData; +// contentBodyParentData.offset = +// Offset(0, _enableHeaderOverlap ? 0.0 : headerTileHeight); +// +// final MultiChildLayoutParentData headerTileParentData = +// _headerTile.parentData; +// headerTileParentData.offset = +// Offset(0, max(0, min(-headerTileOffset, height - headerTileHeight))); +// +// } +// +// double getHeaderTileStuckOffset() { +// final scrollableContent = _scrollable.context.findRenderObject(); +// if (scrollableContent.attached) { +// try { +// return localToGlobal(Offset.zero, ancestor: scrollableContent).dy; +// // ignore: avoid_catches_without_on_clauses +// } catch (e) { +// print(e); +// } +// } +// return 0; +// } +// +// +// @override +// void setupParentData(RenderObject child) { +// super.setupParentData(child); +// child.parentData = MultiChildLayoutParentData(); +// } +// +// @override +// void paint(PaintingContext context, Offset offset) { +// defaultPaint(context, offset); +// } +// } diff --git a/lib/getwidget.dart b/lib/getwidget.dart index 69e3ab12..207aa2b3 100644 --- a/lib/getwidget.dart +++ b/lib/getwidget.dart @@ -39,6 +39,7 @@ export 'package:getwidget/components/tabs/gf_tabs.dart'; export 'package:getwidget/components/toast/gf_toast.dart'; export 'package:getwidget/components/toggle/gf_toggle.dart'; export 'package:getwidget/components/typography/gf_typography.dart'; +export 'package:getwidget/components/sticky_header/gf_sticky_header.dart'; export 'colors/gf_color.dart'; export 'direction/gf_shimmer_direction.dart'; From 6971700e086197f79485dc0153cbf01d0bf634d8 Mon Sep 17 00:00:00 2001 From: deepikahr Date: Mon, 21 Sep 2020 19:04:14 +0530 Subject: [PATCH 02/13] alignment offset changes --- example/lib/main_temp.dart | 13 +++++++------ lib/components/sticky_header/gf_sticky_header.dart | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/example/lib/main_temp.dart b/example/lib/main_temp.dart index 800bd766..5d5b2cf5 100644 --- a/example/lib/main_temp.dart +++ b/example/lib/main_temp.dart @@ -135,8 +135,8 @@ class _MyHomePageState extends State return GFStickyHeader( direction: Axis.horizontal, header: Container( - height: 50.0, - width: 50, + // height: 50.0, + width: 200, color: Colors.blueGrey[700], padding: EdgeInsets.symmetric(horizontal: 16.0), alignment: Alignment.centerLeft, @@ -146,12 +146,13 @@ class _MyHomePageState extends State ), ), content: Container( - height: 100, - width: 100, + // height: 100, + width: 200, child: Image.network(imageList[index], fit: BoxFit.cover, - width: 100, - height: 200.0), + // width: 100, + // height: 200.0 + ), ), ); } diff --git a/lib/components/sticky_header/gf_sticky_header.dart b/lib/components/sticky_header/gf_sticky_header.dart index 4145f881..550aeeca 100644 --- a/lib/components/sticky_header/gf_sticky_header.dart +++ b/lib/components/sticky_header/gf_sticky_header.dart @@ -974,9 +974,9 @@ class RenderGFStickyHeader extends RenderBox with ContainerRenderObjectMixin Date: Mon, 21 Sep 2020 19:39:24 +0530 Subject: [PATCH 03/13] alignment axis changes --- example/lib/main_temp.dart | 11 +- .../sticky_header/gf_sticky_header.dart | 266 ++---------------- 2 files changed, 31 insertions(+), 246 deletions(-) diff --git a/example/lib/main_temp.dart b/example/lib/main_temp.dart index 5d5b2cf5..f569399d 100644 --- a/example/lib/main_temp.dart +++ b/example/lib/main_temp.dart @@ -133,10 +133,13 @@ class _MyHomePageState extends State itemBuilder: (context, index) { var stuckAmount = 1.0 - index.clamp(0.0, 1.0); return GFStickyHeader( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.center, + // enableHeaderOverlap: true, direction: Axis.horizontal, header: Container( - // height: 50.0, - width: 200, + height: 50.0, + width: 50, color: Colors.blueGrey[700], padding: EdgeInsets.symmetric(horizontal: 16.0), alignment: Alignment.centerLeft, @@ -146,8 +149,8 @@ class _MyHomePageState extends State ), ), content: Container( - // height: 100, - width: 200, + height: 100, + width: 100, child: Image.network(imageList[index], fit: BoxFit.cover, // width: 100, diff --git a/lib/components/sticky_header/gf_sticky_header.dart b/lib/components/sticky_header/gf_sticky_header.dart index 550aeeca..8ab3ad86 100644 --- a/lib/components/sticky_header/gf_sticky_header.dart +++ b/lib/components/sticky_header/gf_sticky_header.dart @@ -399,50 +399,8 @@ class RenderGFStickyHeader extends RenderBox with ContainerRenderObjectMixin _overflow > precisionErrorTolerance; /// {@macro flutter.widgets.Clip} /// @@ -467,122 +425,6 @@ class RenderGFStickyHeader extends RenderBox with ContainerRenderObjectMixin 0) { - // final double flexFraction = childSize(child, extent) / _getFlex(child); - // maxFlexFractionSoFar = math.max(maxFlexFractionSoFar, flexFraction); - // } else { - // inflexibleSpace += childSize(child, extent); - // } - // final FlexParentData childParentData = child.parentData as FlexParentData; - // child = childParentData.nextSibling; - // } - // return maxFlexFractionSoFar * totalFlex + inflexibleSpace; - // } else { - // // INTRINSIC CROSS SIZE - // // Intrinsic cross size is the max of the intrinsic cross sizes of the - // // children, after the flexible children are fit into the available space, - // // with the children sized using their max intrinsic dimensions. - // - // // Get inflexible space using the max intrinsic dimensions of fixed children in the main direction. - // final double availableMainSpace = extent; - // int totalFlex = 0; - // double inflexibleSpace = 0.0; - // double maxCrossSize = 0.0; - // RenderBox child = firstChild; - // while (child != null) { - // final int flex = _getFlex(child); - // totalFlex += flex; - // double mainSize; - // double crossSize; - // if (flex == 0) { - // switch (_direction) { - // case Axis.horizontal: - // mainSize = child.getMaxIntrinsicWidth(double.infinity); - // crossSize = childSize(child, mainSize); - // break; - // case Axis.vertical: - // mainSize = child.getMaxIntrinsicHeight(double.infinity); - // crossSize = childSize(child, mainSize); - // break; - // } - // inflexibleSpace += mainSize; - // maxCrossSize = math.max(maxCrossSize, crossSize); - // } - // final FlexParentData childParentData = child.parentData as FlexParentData; - // child = childParentData.nextSibling; - // } - // - // // Determine the spacePerFlex by allocating the remaining available space. - // // When you're overconstrained spacePerFlex can be negative. - // final double spacePerFlex = math.max(0.0, - // (availableMainSpace - inflexibleSpace) / totalFlex); - // - // // Size remaining (flexible) items, find the maximum cross size. - // child = firstChild; - // while (child != null) { - // final int flex = _getFlex(child); - // if (flex > 0) - // maxCrossSize = math.max(maxCrossSize, childSize(child, spacePerFlex * flex)); - // final FlexParentData childParentData = child.parentData as FlexParentData; - // child = childParentData.nextSibling; - // } - // - // return maxCrossSize; - // } - // } - - // @override - // double computeMinIntrinsicWidth(double height) { - // return _getIntrinsicSize( - // sizingDirection: Axis.horizontal, - // extent: height, - // childSize: (RenderBox child, double extent) => child.getMinIntrinsicWidth(extent), - // ); - // } - // - // @override - // double computeMaxIntrinsicWidth(double height) { - // return _getIntrinsicSize( - // sizingDirection: Axis.horizontal, - // extent: height, - // childSize: (RenderBox child, double extent) => child.getMaxIntrinsicWidth(extent), - // ); - // } - // - // @override - // double computeMinIntrinsicHeight(double width) { - // return _getIntrinsicSize( - // sizingDirection: Axis.vertical, - // extent: width, - // childSize: (RenderBox child, double extent) => child.getMinIntrinsicHeight(extent), - // ); - // } - // - // @override - // double computeMaxIntrinsicHeight(double width) { - // return _getIntrinsicSize( - // sizingDirection: Axis.vertical, - // extent: width, - // childSize: (RenderBox child, double extent) => child.getMaxIntrinsicHeight(extent), - // ); - // } - @override double computeDistanceToActualBaseline(TextBaseline baseline) { if (_direction == Axis.horizontal) @@ -668,22 +510,22 @@ class RenderGFStickyHeader extends RenderBox with ContainerRenderObjectMixin debugOverflowHints = [ - // ErrorDescription( - // 'The overflowing $runtimeType has an orientation of $_direction.' - // ), - // ErrorDescription( - // 'The edge of the $runtimeType that is overflowing has been marked ' - // 'in the rendering with a yellow and black striped pattern. This is ' - // 'usually caused by the contents being too big for the $runtimeType.' - // ), - // ErrorHint( - // 'Consider applying a flex factor (e.g. using an Expanded widget) to ' - // 'force the children of the $runtimeType to fit within the available ' - // 'space instead of being sized to their natural size.' - // ), - // ErrorHint( - // 'This is considered an error condition because it indicates that there ' - // 'is content that cannot be seen. If the content is legitimately bigger ' - // 'than the available space, consider clipping it with a ClipRect widget ' - // 'before putting it in the flex, or using a scrollable container rather ' - // 'than a Flex, like a ListView.' - // ), - // ]; - // - // // Simulate a child rect that overflows by the right amount. This child - // // rect is never used for drawing, just for determining the overflow - // // location and amount. - // Rect overflowChildRect; - // switch (_direction) { - // case Axis.horizontal: - // overflowChildRect = Rect.fromLTWH(0.0, 0.0, size.width + _overflow, 0.0); - // break; - // case Axis.vertical: - // overflowChildRect = Rect.fromLTWH(0.0, 0.0, 0.0, size.height + _overflow); - // break; - // } - // paintOverflowIndicator(context, offset, Offset.zero & size, overflowChildRect, overflowHints: debugOverflowHints); - // return true; - // }()); } - // @override - // Rect describeApproximatePaintClip(RenderObject child) => _hasOverflow ? Offset.zero & size : null; - // - // @override - // String toStringShort() { - // String header = super.toStringShort(); - // if (_overflow is double && _hasOverflow) - // header += ' OVERFLOWING'; - // return header; - // } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { From cc0a2bfc3c56f5386fdaeab6f44b9ec8e004c134 Mon Sep 17 00:00:00 2001 From: deepikahr Date: Mon, 21 Sep 2020 19:44:30 +0530 Subject: [PATCH 04/13] alignment offset issue fixed --- lib/components/sticky_header/gf_sticky_header.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/components/sticky_header/gf_sticky_header.dart b/lib/components/sticky_header/gf_sticky_header.dart index 8ab3ad86..2f3a1b2c 100644 --- a/lib/components/sticky_header/gf_sticky_header.dart +++ b/lib/components/sticky_header/gf_sticky_header.dart @@ -817,7 +817,7 @@ class RenderGFStickyHeader extends RenderBox with ContainerRenderObjectMixin Date: Mon, 21 Sep 2020 20:01:49 +0530 Subject: [PATCH 05/13] enable overlap with alignment offset issue fixed --- example/lib/main_temp.dart | 2 +- lib/components/sticky_header/gf_sticky_header.dart | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/example/lib/main_temp.dart b/example/lib/main_temp.dart index f569399d..2f083802 100644 --- a/example/lib/main_temp.dart +++ b/example/lib/main_temp.dart @@ -135,7 +135,7 @@ class _MyHomePageState extends State return GFStickyHeader( crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.center, - // enableHeaderOverlap: true, + enableHeaderOverlap: true, direction: Axis.horizontal, header: Container( height: 50.0, diff --git a/lib/components/sticky_header/gf_sticky_header.dart b/lib/components/sticky_header/gf_sticky_header.dart index 2f3a1b2c..da9a1c4f 100644 --- a/lib/components/sticky_header/gf_sticky_header.dart +++ b/lib/components/sticky_header/gf_sticky_header.dart @@ -185,6 +185,7 @@ class GFStickyHeader extends MultiChildRenderObjectWidget { textBaseline: textBaseline, clipBehavior: clipBehavior, scrollable: scrollable, + enableHeaderOverlap: enableHeaderOverlap ); } } From 050a12df2b9321837b7005f206313072f25986c9 Mon Sep 17 00:00:00 2001 From: deepikahr Date: Wed, 23 Sep 2020 20:08:21 +0530 Subject: [PATCH 06/13] gfstickyheader complted --- example/lib/main_temp.dart | 192 ++-- .../sticky_header/gf_sticky_header.dart | 957 +----------------- .../gf_sticky_header_builder.dart | 78 ++ .../render_gf_sticky_header.dart | 260 +++++ lib/getwidget.dart | 4 +- 5 files changed, 473 insertions(+), 1018 deletions(-) create mode 100644 lib/components/sticky_header/gf_sticky_header_builder.dart create mode 100644 lib/components/sticky_header/render_gf_sticky_header.dart diff --git a/example/lib/main_temp.dart b/example/lib/main_temp.dart index 2f083802..853c0308 100644 --- a/example/lib/main_temp.dart +++ b/example/lib/main_temp.dart @@ -8,7 +8,13 @@ final List imageList = [ 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', 'https://cdn.pixabay.com/photo/2019/12/19/10/55/christmas-market-4705877_960_720.jpg', 'https://cdn.pixabay.com/photo/2019/12/20/00/03/road-4707345_960_720.jpg', - 'https://cdn.pixabay.com/photo/2019/12/22/04/18/x-mas-4711785__340.jpg', + 'https://cdn.pixabay.com/photo/2016/11/22/07/09/spruce-1848543__340.jpg', + 'https://cdn.pixabay.com/photo/2017/12/03/18/04/christmas-balls-2995437_960_720.jpg', + 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', + 'https://cdn.pixabay.com/photo/2017/12/03/18/04/christmas-balls-2995437_960_720.jpg', + 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', + 'https://cdn.pixabay.com/photo/2019/12/19/10/55/christmas-market-4705877_960_720.jpg', + 'https://cdn.pixabay.com/photo/2019/12/20/00/03/road-4707345_960_720.jpg', 'https://cdn.pixabay.com/photo/2016/11/22/07/09/spruce-1848543__340.jpg', 'https://cdn.pixabay.com/photo/2017/12/03/18/04/christmas-balls-2995437_960_720.jpg', 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', @@ -19,13 +25,13 @@ void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) => MaterialApp( - title: 'GetWidget Example', - theme: ThemeData( - primarySwatch: Colors.blue, - ), - debugShowCheckedModeBanner: false, - home: const MyHomePage(title: 'GetWidget Example'), - ); + title: 'GetWidget Example', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + debugShowCheckedModeBanner: false, + home: const MyHomePage(title: 'GetWidget Example'), + ); } class MyHomePage extends StatefulWidget { @@ -75,31 +81,14 @@ class _MyHomePageState extends State 'Xamarin2', ]; - final List imageList = [ - 'https://cdn.pixabay.com/photo/2017/12/03/18/04/christmas-balls-2995437_960_720.jpg', - 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', - 'https://cdn.pixabay.com/photo/2019/12/19/10/55/christmas-market-4705877_960_720.jpg', - 'https://cdn.pixabay.com/photo/2019/12/20/00/03/road-4707345_960_720.jpg', - 'https://cdn.pixabay.com/photo/2016/11/22/07/09/spruce-1848543__340.jpg', - 'https://cdn.pixabay.com/photo/2017/12/03/18/04/christmas-balls-2995437_960_720.jpg', - 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', - 'https://cdn.pixabay.com/photo/2017/12/03/18/04/christmas-balls-2995437_960_720.jpg', - 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', - 'https://cdn.pixabay.com/photo/2019/12/19/10/55/christmas-market-4705877_960_720.jpg', - 'https://cdn.pixabay.com/photo/2019/12/20/00/03/road-4707345_960_720.jpg', - 'https://cdn.pixabay.com/photo/2016/11/22/07/09/spruce-1848543__340.jpg', - 'https://cdn.pixabay.com/photo/2017/12/03/18/04/christmas-balls-2995437_960_720.jpg', - 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', - ]; - void _persistentBottomSheet() { _scaffoldKey.currentState.showBottomSheet((context) => Container( - color: Colors.redAccent, - height: 250, - child: const Center( - child: Text('Hey! guys , this is a persistent bottom sheet'), - ), - )); + color: Colors.redAccent, + height: 250, + child: const Center( + child: Text('Hey! guys , this is a persistent bottom sheet'), + ), + )); } void _modalBottomSheetMenu() { @@ -107,67 +96,106 @@ class _MyHomePageState extends State context: context, elevation: 10, builder: (builder) => Container( - height: 350, - color: Colors.transparent, - child: Container( - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10), - topRight: Radius.circular(10))), - child: const Center( - child: Text('This is a modal sheet'), - )), - )); + height: 350, + color: Colors.transparent, + child: Container( + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10))), + child: const Center( + child: Text('This is a modal sheet'), + )), + )); } @override Widget build(BuildContext context) => Scaffold( - appBar: GFAppBar( - title: const Text('UI KIt'), - ), - body: + appBar: GFAppBar( + title: const Text('UI KIt'), + ), + body: - ListView.builder( - itemCount: imageList.length, - itemBuilder: (context, index) { - var stuckAmount = 1.0 - index.clamp(0.0, 1.0); - return GFStickyHeader( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: MainAxisAlignment.center, - enableHeaderOverlap: true, - direction: Axis.horizontal, - header: Container( - height: 50.0, - width: 50, - color: Colors.blueGrey[700], - padding: EdgeInsets.symmetric(horizontal: 16.0), - alignment: Alignment.centerLeft, - child: Text( - 'H $index', - style: const TextStyle(color: Colors.white), - ), - ), - content: Container( - height: 100, - width: 100, - child: Image.network(imageList[index], - fit: BoxFit.cover, - // width: 100, - // height: 200.0 - ), - ), - ); - } - ), + ListView.builder( + + itemBuilder: (context, index) => GFStickyHeaderBuilder( + direction: Axis.horizontal, + // enableHeaderOverlap: true, + stickyContentPosition: GFPosition.start, + stickyContentBuilder: (BuildContext context, double stuckAmount) { + stuckAmount = 1.0 - stuckAmount.clamp(0.0, 1.0); + return Container( + height: 50.0, + width: 200, + color: + Colors.grey[900].withOpacity(0.6 + stuckAmount * 0.4), + padding: EdgeInsets.symmetric(horizontal: 16.0), + alignment: Alignment.centerLeft, + child: Text( + 'Header #$index', + style: const TextStyle(color: Colors.white), + ), + ); + }, + content: Container( + // margin: EdgeInsets.all(8), + height: 200, + width: 200, + color: Colors.teal, + child: Image.network(imageList[index], + fit: BoxFit.cover, + width: 100, + height: 200.0 + ), + ), + ) + ) ); + // Container( + // height: 600, + // color: Colors.teal, + // child: ListView.builder( + // itemCount: imageList.length, + // itemBuilder: (context, index) => GFStickyHeader( + // // enableHeaderOverlap: true, + // direction: Axis.vertical, + // stickyContentPosition: GFPosition.end, + // stickyContent: Container( + // // alignment: AlignmentDirectional.centerEnd, + // child: Container( + // alignment: AlignmentDirectional.center, + // height: 50, + // width: 100, + // color: Colors.blueGrey[700], + // padding: EdgeInsets.symmetric(horizontal: 16.0), + // child: Text( + // 'H $index', + // style: const TextStyle(color: Colors.white), + // ), + // ), + // ), + // content: Container( + // height: 100, + // width: 200, + // child: Image.network(imageList[index], + // fit: BoxFit.cover, + // // width: 100, + // // height: 200.0 + // ), + // ), + // ) + // ), + // ) + // ); + // ListView( // children: [ // Image.network(imageList[3], height: 200, fit: BoxFit.cover,), // Image.network(imageList[4], height: 200, fit: BoxFit.cover,), // GFStickyHeader( -// header: Container( +// stickyContent: Container( // height: 100, // color: Colors.teal, // padding: const EdgeInsets.symmetric(horizontal: 16), @@ -280,9 +308,6 @@ class _MyHomePageState extends State // ); } - - - // void main() => runApp(MyApp()); // // class MyApp extends StatelessWidget { @@ -461,4 +486,3 @@ class _MyHomePageState extends State // ], // ), // ))); - diff --git a/lib/components/sticky_header/gf_sticky_header.dart b/lib/components/sticky_header/gf_sticky_header.dart index da9a1c4f..586783b9 100644 --- a/lib/components/sticky_header/gf_sticky_header.dart +++ b/lib/components/sticky_header/gf_sticky_header.dart @@ -1,174 +1,45 @@ -import 'dart:math' show min, max; - -import 'dart:math' as math; - import 'package:flutter/foundation.dart'; - import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:meta/meta.dart'; +import 'package:getwidget/getwidget.dart'; class GFStickyHeader extends MultiChildRenderObjectWidget { GFStickyHeader({ Key key, - this.header, this.content, this.enableHeaderOverlap = false, - @required this.direction, - this.mainAxisAlignment = MainAxisAlignment.start, - this.mainAxisSize = MainAxisSize.max, - this.crossAxisAlignment = CrossAxisAlignment.center, - this.textDirection, - this.verticalDirection = VerticalDirection.down, - this.textBaseline, - this.clipBehavior = Clip.hardEdge, - // List children = const [], + @required this.stickyContent, + @required this.content, + this.direction = Axis.vertical, + this.enableHeaderOverlap = false, + this.callback, + this.stickyContentPosition = GFPosition.start }) : assert(direction != null), - assert(mainAxisAlignment != null), - assert(mainAxisSize != null), - assert(crossAxisAlignment != null), - assert(verticalDirection != null), - assert(crossAxisAlignment != CrossAxisAlignment.baseline || textBaseline != null), - assert(clipBehavior != null), - super(key: key, children: [content, header]); - // Key key, - // @required this.header, - // @required this.content, - // this.enableHeaderOverlap = false, - // }) : super( - // key: key, - // children: [content, header], - // ); + super(key: key, children: stickyContentPosition == GFPosition.start ? [stickyContent, content] : [content, stickyContent] + ); - /// widget can be used to define [header]. - final Widget header; + /// widget can be used to define [stickyContent]. + final Widget stickyContent; /// widget can be used to define [content]. final Widget content; - /// On state true, the [header] will overlap the [content]. + /// On state true, the [stickyContent] will overlap the [content]. /// Default value is false. final bool enableHeaderOverlap; - /// - /// If you know the axis in advance, then consider using a [Row] (if it's - /// horizontal) or [Column] (if it's vertical) instead of a [Flex], since that - /// will be less verbose. (For [Row] and [Column] this property is fixed to - /// the appropriate axis.) - final Axis direction; - - /// How the children should be placed along the main axis. - /// - /// For example, [MainAxisAlignment.start], the default, places the children - /// at the start (i.e., the left for a [Row] or the top for a [Column]) of the - /// main axis. - final MainAxisAlignment mainAxisAlignment; - - /// How much space should be occupied in the main axis. - /// - /// After allocating space to children, there might be some remaining free - /// space. This value controls whether to maximize or minimize the amount of - /// free space, subject to the incoming layout constraints. - /// - /// If some children have a non-zero flex factors (and none have a fit of - /// [FlexFit.loose]), they will expand to consume all the available space and - /// there will be no remaining free space to maximize or minimize, making this - /// value irrelevant to the final layout. - final MainAxisSize mainAxisSize; - - /// How the children should be placed along the cross axis. - /// - /// For example, [CrossAxisAlignment.center], the default, centers the - /// children in the cross axis (e.g., horizontally for a [Column]). - final CrossAxisAlignment crossAxisAlignment; - - /// Determines the order to lay children out horizontally and how to interpret - /// `start` and `end` in the horizontal direction. - /// - /// Defaults to the ambient [Directionality]. - /// - /// If the [direction] is [Axis.horizontal], this controls the order in which - /// the children are positioned (left-to-right or right-to-left), and the - /// meaning of the [mainAxisAlignment] property's [MainAxisAlignment.start] and - /// [MainAxisAlignment.end] values. - /// - /// If the [direction] is [Axis.horizontal], and either the - /// [mainAxisAlignment] is either [MainAxisAlignment.start] or - /// [MainAxisAlignment.end], or there's more than one child, then the - /// [textDirection] (or the ambient [Directionality]) must not be null. - /// - /// If the [direction] is [Axis.vertical], this controls the meaning of the - /// [crossAxisAlignment] property's [CrossAxisAlignment.start] and - /// [CrossAxisAlignment.end] values. - /// - /// If the [direction] is [Axis.vertical], and the [crossAxisAlignment] is - /// either [CrossAxisAlignment.start] or [CrossAxisAlignment.end], then the - /// [textDirection] (or the ambient [Directionality]) must not be null. - final TextDirection textDirection; - - /// Determines the order to lay children out vertically and how to interpret - /// `start` and `end` in the vertical direction. - /// - /// Defaults to [VerticalDirection.down]. - /// - /// If the [direction] is [Axis.vertical], this controls which order children - /// are painted in (down or up), the meaning of the [mainAxisAlignment] - /// property's [MainAxisAlignment.start] and [MainAxisAlignment.end] values. - /// - /// If the [direction] is [Axis.vertical], and either the [mainAxisAlignment] - /// is either [MainAxisAlignment.start] or [MainAxisAlignment.end], or there's - /// more than one child, then the [verticalDirection] must not be null. - /// - /// If the [direction] is [Axis.horizontal], this controls the meaning of the - /// [crossAxisAlignment] property's [CrossAxisAlignment.start] and - /// [CrossAxisAlignment.end] values. - /// - /// If the [direction] is [Axis.horizontal], and the [crossAxisAlignment] is - /// either [CrossAxisAlignment.start] or [CrossAxisAlignment.end], then the - /// [verticalDirection] must not be null. - final VerticalDirection verticalDirection; - - /// If aligning items according to their baseline, which baseline to use. - final TextBaseline textBaseline; - - // TODO(liyuqian): defaults to [Clip.none] once Google references are updated. - /// {@macro flutter.widgets.Clip} - /// - /// Defaults to [Clip.hardEdge]. - final Clip clipBehavior; + /// [GFPosition] allows to [stickyContentPosition] to stick at top in [Axis.vertical] and stick at start in [Axis.horizontal] + /// Defaults to [GFPosition.start] + final GFPosition stickyContentPosition; - bool get _needTextDirection { - assert(direction != null); - switch (direction) { - case Axis.horizontal: - return true; // because it affects the layout order. - case Axis.vertical: - assert(crossAxisAlignment != null); - return crossAxisAlignment == CrossAxisAlignment.start - || crossAxisAlignment == CrossAxisAlignment.end; - } - return null; - } + /// Optional callback with stickyness value. If you think you need this, then you might want to + /// consider using [StickyHeaderBuilder] instead. + final RenderGFStickyHeaderCallback callback; - /// The value to pass to [RenderFlex.textDirection]. - /// - /// This value is derived from the [textDirection] property and the ambient - /// [Directionality]. The value is null if there is no need to specify the - /// text direction. In practice there's always a need to specify the direction - /// except for vertical flexes (e.g. [Column]s) whose [crossAxisAlignment] is - /// not dependent on the text direction (not `start` or `end`). In particular, - /// a [Row] always needs a text direction because the text direction controls - /// its layout order. (For [Column]s, the layout order is controlled by - /// [verticalDirection], which is always specified as it does not depend on an - /// inherited widget and defaults to [VerticalDirection.down].) - /// - /// This method exists so that subclasses of [Flex] that create their own - /// render objects that are derived from [RenderFlex] can do so and still use - /// the logic for providing a text direction only when it is necessary. - @protected - TextDirection getEffectiveTextDirection(BuildContext context) { - return textDirection ?? (_needTextDirection ? Directionality.of(context) : null); - } + /// [direction] allows children to align in vertical / horizontal way + /// Defaults to [Axis.vertical] + final Axis direction; @override @@ -177,794 +48,14 @@ class GFStickyHeader extends MultiChildRenderObjectWidget { assert(scrollable != null); return RenderGFStickyHeader( direction: direction, - mainAxisAlignment: mainAxisAlignment, - mainAxisSize: mainAxisSize, - crossAxisAlignment: crossAxisAlignment, - textDirection: getEffectiveTextDirection(context), - verticalDirection: verticalDirection, - textBaseline: textBaseline, - clipBehavior: clipBehavior, scrollable: scrollable, - enableHeaderOverlap: enableHeaderOverlap + enableHeaderOverlap: enableHeaderOverlap, + callback: callback, + stickyContentPosition: stickyContentPosition, ); } } -bool _startIsTopLeft(Axis direction, TextDirection textDirection, VerticalDirection verticalDirection) { - assert(direction != null); - // If the relevant value of textDirection or verticalDirection is null, this returns null too. - switch (direction) { - case Axis.horizontal: - switch (textDirection) { - case TextDirection.ltr: - return true; - case TextDirection.rtl: - return false; - } - break; - case Axis.vertical: - switch (verticalDirection) { - case VerticalDirection.down: - return true; - case VerticalDirection.up: - return false; - } - break; - } - return null; -} - - -typedef _ChildSizingFunction = double Function(RenderBox child, double extent); - -class RenderGFStickyHeader extends RenderBox with ContainerRenderObjectMixin, - RenderBoxContainerDefaultsMixin, - DebugOverflowIndicatorMixin { - /// Creates a flex render object. - /// - /// By default, the flex layout is horizontal and children are aligned to the - /// start of the main axis and the center of the cross axis. - RenderGFStickyHeader({ - List children, - Axis direction = Axis.horizontal, - MainAxisSize mainAxisSize = MainAxisSize.max, - MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, - CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, - TextDirection textDirection, - VerticalDirection verticalDirection = VerticalDirection.down, - TextBaseline textBaseline, - Clip clipBehavior = Clip.none, - bool enableHeaderOverlap = false, - ScrollableState scrollable, - }) : assert(direction != null), - assert(mainAxisAlignment != null), - assert(mainAxisSize != null), - assert(crossAxisAlignment != null), - assert(clipBehavior != null), - assert(scrollable != null), - _scrollable = scrollable, - _direction = direction, - _mainAxisAlignment = mainAxisAlignment, - _mainAxisSize = mainAxisSize, - _crossAxisAlignment = crossAxisAlignment, - _textDirection = textDirection, - _verticalDirection = verticalDirection, - _textBaseline = textBaseline, - _enableHeaderOverlap = enableHeaderOverlap { - addAll(children); - } - // _clipBehavior = clipBehavior { - // addAll(children); - // } - - - - final ScrollableState _scrollable; - final bool _enableHeaderOverlap; - - /// The direction to use as the main axis. - Axis get direction => _direction; - Axis _direction; - set direction(Axis value) { - assert(value != null); - if (_direction != value) { - _direction = value; - markNeedsLayout(); - } - } - - /// How the children should be placed along the main axis. - /// - /// If the [direction] is [Axis.horizontal], and the [mainAxisAlignment] is - /// either [MainAxisAlignment.start] or [MainAxisAlignment.end], then the - /// [textDirection] must not be null. - /// - /// If the [direction] is [Axis.vertical], and the [mainAxisAlignment] is - /// either [MainAxisAlignment.start] or [MainAxisAlignment.end], then the - /// [verticalDirection] must not be null. - MainAxisAlignment get mainAxisAlignment => _mainAxisAlignment; - MainAxisAlignment _mainAxisAlignment; - set mainAxisAlignment(MainAxisAlignment value) { - assert(value != null); - if (_mainAxisAlignment != value) { - _mainAxisAlignment = value; - markNeedsLayout(); - } - } - - /// How much space should be occupied in the main axis. - /// - /// After allocating space to children, there might be some remaining free - /// space. This value controls whether to maximize or minimize the amount of - /// free space, subject to the incoming layout constraints. - /// - /// If some children have a non-zero flex factors (and none have a fit of - /// [FlexFit.loose]), they will expand to consume all the available space and - /// there will be no remaining free space to maximize or minimize, making this - /// value irrelevant to the final layout. - MainAxisSize get mainAxisSize => _mainAxisSize; - MainAxisSize _mainAxisSize; - set mainAxisSize(MainAxisSize value) { - assert(value != null); - if (_mainAxisSize != value) { - _mainAxisSize = value; - markNeedsLayout(); - } - } - - /// How the children should be placed along the cross axis. - /// - /// If the [direction] is [Axis.horizontal], and the [crossAxisAlignment] is - /// either [CrossAxisAlignment.start] or [CrossAxisAlignment.end], then the - /// [verticalDirection] must not be null. - /// - /// If the [direction] is [Axis.vertical], and the [crossAxisAlignment] is - /// either [CrossAxisAlignment.start] or [CrossAxisAlignment.end], then the - /// [textDirection] must not be null. - CrossAxisAlignment get crossAxisAlignment => _crossAxisAlignment; - CrossAxisAlignment _crossAxisAlignment; - set crossAxisAlignment(CrossAxisAlignment value) { - assert(value != null); - if (_crossAxisAlignment != value) { - _crossAxisAlignment = value; - markNeedsLayout(); - } - } - - /// Determines the order to lay children out horizontally and how to interpret - /// `start` and `end` in the horizontal direction. - /// - /// If the [direction] is [Axis.horizontal], this controls the order in which - /// children are positioned (left-to-right or right-to-left), and the meaning - /// of the [mainAxisAlignment] property's [MainAxisAlignment.start] and - /// [MainAxisAlignment.end] values. - /// - /// If the [direction] is [Axis.horizontal], and either the - /// [mainAxisAlignment] is either [MainAxisAlignment.start] or - /// [MainAxisAlignment.end], or there's more than one child, then the - /// [textDirection] must not be null. - /// - /// If the [direction] is [Axis.vertical], this controls the meaning of the - /// [crossAxisAlignment] property's [CrossAxisAlignment.start] and - /// [CrossAxisAlignment.end] values. - /// - /// If the [direction] is [Axis.vertical], and the [crossAxisAlignment] is - /// either [CrossAxisAlignment.start] or [CrossAxisAlignment.end], then the - /// [textDirection] must not be null. - TextDirection get textDirection => _textDirection; - TextDirection _textDirection; - set textDirection(TextDirection value) { - if (_textDirection != value) { - _textDirection = value; - markNeedsLayout(); - } - } - - /// Determines the order to lay children out vertically and how to interpret - /// `start` and `end` in the vertical direction. - /// - /// If the [direction] is [Axis.vertical], this controls which order children - /// are painted in (down or up), the meaning of the [mainAxisAlignment] - /// property's [MainAxisAlignment.start] and [MainAxisAlignment.end] values. - /// - /// If the [direction] is [Axis.vertical], and either the [mainAxisAlignment] - /// is either [MainAxisAlignment.start] or [MainAxisAlignment.end], or there's - /// more than one child, then the [verticalDirection] must not be null. - /// - /// If the [direction] is [Axis.horizontal], this controls the meaning of the - /// [crossAxisAlignment] property's [CrossAxisAlignment.start] and - /// [CrossAxisAlignment.end] values. - /// - /// If the [direction] is [Axis.horizontal], and the [crossAxisAlignment] is - /// either [CrossAxisAlignment.start] or [CrossAxisAlignment.end], then the - /// [verticalDirection] must not be null. - VerticalDirection get verticalDirection => _verticalDirection; - VerticalDirection _verticalDirection; - set verticalDirection(VerticalDirection value) { - if (_verticalDirection != value) { - _verticalDirection = value; - markNeedsLayout(); - } - } - - /// If aligning items according to their baseline, which baseline to use. - /// - /// Must not be null if [crossAxisAlignment] is [CrossAxisAlignment.baseline]. - TextBaseline get textBaseline => _textBaseline; - TextBaseline _textBaseline; - set textBaseline(TextBaseline value) { - assert(_crossAxisAlignment != CrossAxisAlignment.baseline || value != null); - if (_textBaseline != value) { - _textBaseline = value; - markNeedsLayout(); - } - } - - // Set during layout if overflow occurred on the main axis. - double _overflow; - - /// {@macro flutter.widgets.Clip} - /// - /// Defaults to [Clip.none], and must not be null. - Clip get clipBehavior => _clipBehavior; - Clip _clipBehavior = Clip.none; - set clipBehavior(Clip value) { - assert(value != null); - if (value != _clipBehavior) { - _clipBehavior = value; - markNeedsPaint(); - markNeedsSemanticsUpdate(); - } - } - - @override - void setupParentData(RenderBox child) { - if (child.parentData is! FlexParentData) { - child.parentData = FlexParentData(); - } - // super.setupParentData(child); - // child.parentData = MultiChildLayoutParentData(); - } - - @override - double computeDistanceToActualBaseline(TextBaseline baseline) { - if (_direction == Axis.horizontal) - return defaultComputeDistanceToHighestActualBaseline(baseline); - return defaultComputeDistanceToFirstActualBaseline(baseline); - } - - int _getFlex(RenderBox child) { - final FlexParentData childParentData = child.parentData as FlexParentData; - return childParentData.flex ?? 0; - } - - FlexFit _getFit(RenderBox child) { - final FlexParentData childParentData = child.parentData as FlexParentData; - return childParentData.fit ?? FlexFit.tight; - } - - double _getCrossSize(RenderBox child) { - switch (_direction) { - case Axis.horizontal: - return child.size.height; - case Axis.vertical: - return child.size.width; - } - return null; - } - - double _getMainSize(RenderBox child) { - switch (_direction) { - case Axis.horizontal: - return child.size.width; - case Axis.vertical: - return child.size.height; - } - return null; - } - - @override - void attach(PipelineOwner owner) { - super.attach(owner); - _scrollable.position.addListener(markNeedsLayout); - } - - @override - void detach() { - _scrollable.position.removeListener(markNeedsLayout); - super.detach(); - } - RenderBox get _headerTile => lastChild; - RenderBox get _contentBody => firstChild; - - double getHeaderTileStuckOffset() { - final scrollableContent = _scrollable.context.findRenderObject(); - if (scrollableContent.attached) { - try { - return localToGlobal(Offset.zero, ancestor: scrollableContent).dy; - // ignore: avoid_catches_without_on_clauses - } catch (e) { - print(e); - } - } - return 0; - } - - - @override - void performLayout() { - // assert(_debugHasNecessaryDirections); - // final BoxConstraints constraints = this.constraints; - - assert(childCount == 2); - _headerTile.layout(constraints.loosen(), parentUsesSize: true); - _contentBody.layout(constraints.loosen(), parentUsesSize: true); - - final headerTileHeight = _headerTile.size.height; - final contentBodyHeight = _contentBody.size.height; - - final height = max( - constraints.minHeight, - _enableHeaderOverlap - ? contentBodyHeight - : headerTileHeight + contentBodyHeight); - final width = max(constraints.minWidth, contentBodyHeight); - // - // size = Size( - // constraints.constrainWidth(width), constraints.constrainHeight(height)); - // - final double headerTileOffset = getHeaderTileStuckOffset(); - // - // final FlexParentData contentBodyParentData = - // _contentBody.parentData; - // contentBodyParentData.offset = - // Offset(0, _enableHeaderOverlap ? 0.0 : headerTileHeight); - // - // - // final FlexParentData headerTileParentData = - // _headerTile.parentData; - // headerTileParentData.offset = - // Offset(0, max(0, min(-headerTileOffset, height - headerTileHeight))); - - // Determine used flex factor, size inflexible items, calculate free space. - int totalFlex = 0; - int totalChildren = 0; - assert(constraints != null); - final double maxMainSize = _direction == Axis.horizontal ? constraints.maxWidth : constraints.maxHeight; - final bool canFlex = maxMainSize < double.infinity; - - double crossSize = 0.0; - double allocatedSize = 0.0; // Sum of the sizes of the non-flexible children. - RenderBox child = firstChild; - RenderBox lastFlexChild; - while (child != null) { - final FlexParentData childParentData = child.parentData as FlexParentData; - totalChildren++; - final int flex = _getFlex(child); - if (flex > 0) { - assert(() { - final String identity = _direction == Axis.horizontal ? 'row' : 'column'; - final String axis = _direction == Axis.horizontal ? 'horizontal' : 'vertical'; - final String dimension = _direction == Axis.horizontal ? 'width' : 'height'; - DiagnosticsNode error, message; - final List addendum = []; - if (!canFlex && (mainAxisSize == MainAxisSize.max || _getFit(child) == FlexFit.tight)) { - error = ErrorSummary('RenderFlex children have non-zero flex but incoming $dimension constraints are unbounded.'); - message = ErrorDescription( - 'When a $identity is in a parent that does not provide a finite $dimension constraint, for example ' - 'if it is in a $axis scrollable, it will try to shrink-wrap its children along the $axis ' - 'axis. Setting a flex on a child (e.g. using Expanded) indicates that the child is to ' - 'expand to fill the remaining space in the $axis direction.' - ); - RenderBox node = this; - switch (_direction) { - case Axis.horizontal: - while (!node.constraints.hasBoundedWidth && node.parent is RenderBox) - node = node.parent as RenderBox; - if (!node.constraints.hasBoundedWidth) - node = null; - break; - case Axis.vertical: - while (!node.constraints.hasBoundedHeight && node.parent is RenderBox) - node = node.parent as RenderBox; - if (!node.constraints.hasBoundedHeight) - node = null; - break; - } - if (node != null) { - addendum.add(node.describeForError('The nearest ancestor providing an unbounded width constraint is')); - } - addendum.add(ErrorHint('See also: https://flutter.dev/layout/')); - } else { - return true; - } - throw FlutterError.fromParts([ - error, - message, - ErrorDescription( - 'These two directives are mutually exclusive. If a parent is to shrink-wrap its child, the child ' - 'cannot simultaneously expand to fit its parent.' - ), - ErrorHint( - 'Consider setting mainAxisSize to MainAxisSize.min and using FlexFit.loose fits for the flexible ' - 'children (using Flexible rather than Expanded). This will allow the flexible children ' - 'to size themselves to less than the infinite remaining space they would otherwise be ' - 'forced to take, and then will cause the RenderFlex to shrink-wrap the children ' - 'rather than expanding to fit the maximum constraints provided by the parent.' - ), - ErrorDescription( - 'If this message did not help you determine the problem, consider using debugDumpRenderTree():\n' - ' https://flutter.dev/debugging/#rendering-layer\n' - ' http://api.flutter.dev/flutter/rendering/debugDumpRenderTree.html' - ), - describeForError('The affected RenderFlex is', style: DiagnosticsTreeStyle.errorProperty), - DiagnosticsProperty('The creator information is set to', debugCreator, style: DiagnosticsTreeStyle.errorProperty), - ...addendum, - ErrorDescription( - "If none of the above helps enough to fix this problem, please don't hesitate to file a bug:\n" - ' https://github.com/flutter/flutter/issues/new?template=BUG.md' - ), - ]); - }()); - totalFlex += childParentData.flex; - lastFlexChild = child; - } else { - BoxConstraints innerConstraints; - if (crossAxisAlignment == CrossAxisAlignment.stretch) { - switch (_direction) { - case Axis.horizontal: - innerConstraints = BoxConstraints(minHeight: constraints.maxHeight, - maxHeight: constraints.maxHeight); - break; - case Axis.vertical: - innerConstraints = BoxConstraints(minWidth: constraints.maxWidth, - maxWidth: constraints.maxWidth); - break; - } - } else { - switch (_direction) { - case Axis.horizontal: - innerConstraints = BoxConstraints(maxHeight: constraints.maxHeight); - break; - case Axis.vertical: - innerConstraints = BoxConstraints(maxWidth: constraints.maxWidth); - break; - } - } - child.layout(innerConstraints, parentUsesSize: true); - allocatedSize += _getMainSize(child); - crossSize = math.max(crossSize, _getCrossSize(child)); - } - assert(child.parentData == childParentData); - child = childParentData.nextSibling; - } - - // Distribute free space to flexible children, and determine baseline. - final double freeSpace = math.max(0.0, (canFlex ? maxMainSize : 0.0) - allocatedSize); - double allocatedFlexSpace = 0.0; - double maxBaselineDistance = 0.0; - if (totalFlex > 0 || crossAxisAlignment == CrossAxisAlignment.baseline) { - final double spacePerFlex = canFlex && totalFlex > 0 ? (freeSpace / totalFlex) : double.nan; - child = firstChild; - double maxSizeAboveBaseline = 0; - double maxSizeBelowBaseline = 0; - while (child != null) { - final int flex = _getFlex(child); - if (flex > 0) { - final double maxChildExtent = canFlex ? (child == lastFlexChild ? (freeSpace - allocatedFlexSpace) : spacePerFlex * flex) : double.infinity; - double minChildExtent; - switch (_getFit(child)) { - case FlexFit.tight: - assert(maxChildExtent < double.infinity); - minChildExtent = maxChildExtent; - break; - case FlexFit.loose: - minChildExtent = 0.0; - break; - } - assert(minChildExtent != null); - BoxConstraints innerConstraints; - if (crossAxisAlignment == CrossAxisAlignment.stretch) { - switch (_direction) { - case Axis.horizontal: - innerConstraints = BoxConstraints(minWidth: minChildExtent, - maxWidth: maxChildExtent, - minHeight: constraints.maxHeight, - maxHeight: constraints.maxHeight); - break; - case Axis.vertical: - innerConstraints = BoxConstraints(minWidth: constraints.maxWidth, - maxWidth: constraints.maxWidth, - minHeight: minChildExtent, - maxHeight: maxChildExtent); - break; - } - } else { - switch (_direction) { - case Axis.horizontal: - innerConstraints = BoxConstraints(minWidth: minChildExtent, - maxWidth: maxChildExtent, - maxHeight: constraints.maxHeight); - break; - case Axis.vertical: - innerConstraints = BoxConstraints(maxWidth: constraints.maxWidth, - minHeight: minChildExtent, - maxHeight: maxChildExtent); - break; - } - } - child.layout(innerConstraints, parentUsesSize: true); - final double childSize = _getMainSize(child); - assert(childSize <= maxChildExtent); - allocatedSize += childSize; - allocatedFlexSpace += maxChildExtent; - crossSize = math.max(crossSize, _getCrossSize(child)); - } - if (crossAxisAlignment == CrossAxisAlignment.baseline) { - assert(() { - if (textBaseline == null) - throw FlutterError('To use FlexAlignItems.baseline, you must also specify which baseline to use using the "baseline" argument.'); - return true; - }()); - final double distance = child.getDistanceToBaseline(textBaseline, onlyReal: true); - if (distance != null) { - maxBaselineDistance = math.max(maxBaselineDistance, distance); - maxSizeAboveBaseline = math.max( - distance, - maxSizeAboveBaseline, - ); - maxSizeBelowBaseline = math.max( - child.size.height - distance, - maxSizeBelowBaseline, - ); - crossSize = maxSizeAboveBaseline + maxSizeBelowBaseline; - } - } - final FlexParentData childParentData = child.parentData as FlexParentData; - child = childParentData.nextSibling; - } - } - - // Align items along the main axis. - final double idealSize = canFlex && mainAxisSize == MainAxisSize.max ? maxMainSize : allocatedSize; - double actualSize; - double actualSizeDelta; - switch (_direction) { - case Axis.horizontal: - size = constraints.constrain(Size(idealSize, crossSize)); - actualSize = size.width; - crossSize = size.height; - break; - case Axis.vertical: - size = constraints.constrain(Size(crossSize, idealSize)); - actualSize = size.height; - crossSize = size.width; - break; - } - actualSizeDelta = actualSize - allocatedSize; - _overflow = math.max(0.0, -actualSizeDelta); - final double remainingSpace = math.max(0.0, actualSizeDelta); - double leadingSpace; - double betweenSpace; - // flipMainAxis is used to decide whether to lay out left-to-right/top-to-bottom (false), or - // right-to-left/bottom-to-top (true). The _startIsTopLeft will return null if there's only - // one child and the relevant direction is null, in which case we arbitrarily decide not to - // flip, but that doesn't have any detectable effect. - final bool flipMainAxis = !(_startIsTopLeft(direction, textDirection, verticalDirection) ?? true); - switch (_mainAxisAlignment) { - case MainAxisAlignment.start: - leadingSpace = 0.0; - betweenSpace = 0.0; - break; - case MainAxisAlignment.end: - leadingSpace = remainingSpace; - betweenSpace = 0.0; - break; - case MainAxisAlignment.center: - leadingSpace = remainingSpace / 2.0; - betweenSpace = 0.0; - break; - case MainAxisAlignment.spaceBetween: - leadingSpace = 0.0; - betweenSpace = totalChildren > 1 ? remainingSpace / (totalChildren - 1) : 0.0; - break; - case MainAxisAlignment.spaceAround: - betweenSpace = totalChildren > 0 ? remainingSpace / totalChildren : 0.0; - leadingSpace = betweenSpace / 2.0; - break; - case MainAxisAlignment.spaceEvenly: - betweenSpace = totalChildren > 0 ? remainingSpace / (totalChildren + 1) : 0.0; - leadingSpace = betweenSpace; - break; - } - - // Position elements - double childMainPosition = flipMainAxis ? actualSize - leadingSpace : leadingSpace; - child = _contentBody; - while (child != null) { - final FlexParentData childParentData = child.parentData as FlexParentData; - double childCrossPosition; - switch (_crossAxisAlignment) { - case CrossAxisAlignment.start: - case CrossAxisAlignment.end: - childCrossPosition = _startIsTopLeft(flipAxis(direction), textDirection, verticalDirection) - == (_crossAxisAlignment == CrossAxisAlignment.start) - ? 0.0 - : crossSize - _getCrossSize(child); - break; - case CrossAxisAlignment.center: - childCrossPosition = crossSize / 2.0 - _getCrossSize(child) / 2.0; - break; - case CrossAxisAlignment.stretch: - childCrossPosition = 0.0; - break; - case CrossAxisAlignment.baseline: - childCrossPosition = 0.0; - if (_direction == Axis.horizontal) { - assert(textBaseline != null); - final double distance = child.getDistanceToBaseline(textBaseline, onlyReal: true); - if (distance != null) - childCrossPosition = maxBaselineDistance - distance; - } - break; - } - - if (flipMainAxis) - childMainPosition -= _getMainSize(child); - switch (_direction) { - case Axis.horizontal: - final FlexParentData contentBodyParentData = - _contentBody.parentData; - contentBodyParentData.offset = - Offset(0,0); - final FlexParentData headerTileParentData = - _headerTile.parentData; - // headerTileParentData.offset = Offset(childMainPosition, childCrossPosition); - headerTileParentData.offset = - Offset(childMainPosition, max(min(-headerTileOffset, height - headerTileHeight), 0)); - break; - case Axis.vertical: - final FlexParentData contentBodyParentData = - _contentBody.parentData; - contentBodyParentData.offset = - Offset(0, _enableHeaderOverlap ? 0.0 : headerTileHeight); - final FlexParentData headerTileParentData = - _headerTile.parentData; - headerTileParentData.offset = - Offset(0, max(0, min(-headerTileOffset, height - headerTileHeight))); - // childParentData.offset = Offset(0, max(0, min(-headerTileOffset, height - headerTileHeight))); - break; - } - if (flipMainAxis) { - childMainPosition -= betweenSpace; - } else { - childMainPosition += _getMainSize(child) + betweenSpace; - } - child = childParentData.nextSibling; - } - } - - @override - bool hitTestChildren(BoxHitTestResult result, { Offset position }) { - return defaultHitTestChildren(result, position: position); - } - - @override - void paint(PaintingContext context, Offset offset) { - defaultPaint(context, offset); - } - - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add(EnumProperty('direction', direction)); - properties.add(EnumProperty('mainAxisAlignment', mainAxisAlignment)); - properties.add(EnumProperty('mainAxisSize', mainAxisSize)); - properties.add(EnumProperty('crossAxisAlignment', crossAxisAlignment)); - properties.add(EnumProperty('textDirection', textDirection, defaultValue: null)); - properties.add(EnumProperty('verticalDirection', verticalDirection, defaultValue: null)); - properties.add(EnumProperty('textBaseline', textBaseline, defaultValue: null)); - } -} -// class RenderGFStickyHeader extends RenderBox -// with -// ContainerRenderObjectMixin, -// RenderBoxContainerDefaultsMixin { -// RenderGFStickyHeader({ -// @required ScrollableState scrollable, -// bool enableHeaderOverlap = false, -// RenderBox header, -// RenderBox content, -// }) : assert(scrollable != null), -// _scrollable = scrollable, -// _enableHeaderOverlap = enableHeaderOverlap { -// if (content != null) { -// add(content); -// } -// if (header != null) { -// add(header); -// } -// } -// -// final ScrollableState _scrollable; -// final bool _enableHeaderOverlap; -// -// @override -// void attach(PipelineOwner owner) { -// super.attach(owner); -// _scrollable.position.addListener(markNeedsLayout); -// } -// -// @override -// void detach() { -// _scrollable.position.removeListener(markNeedsLayout); -// super.detach(); -// } -// -// RenderBox get _headerTile => lastChild; -// -// RenderBox get _contentBody => firstChild; -// -// @override -// void performLayout() { -// -// assert(childCount == 2); -// _headerTile.layout(constraints.loosen(), parentUsesSize: true); -// _contentBody.layout(constraints.loosen(), parentUsesSize: true); -// -// final headerTileHeight = _headerTile.size.height; -// final contentBodyHeight = _contentBody.size.height; -// -// final height = max( -// constraints.minHeight, -// _enableHeaderOverlap -// ? contentBodyHeight -// : headerTileHeight + contentBodyHeight); -// final width = max(constraints.minWidth, contentBodyHeight); -// -// size = Size( -// constraints.constrainWidth(width), constraints.constrainHeight(height)); -// -// final double headerTileOffset = getHeaderTileStuckOffset(); -// -// final MultiChildLayoutParentData contentBodyParentData = -// _contentBody.parentData; -// contentBodyParentData.offset = -// Offset(0, _enableHeaderOverlap ? 0.0 : headerTileHeight); -// -// final MultiChildLayoutParentData headerTileParentData = -// _headerTile.parentData; -// headerTileParentData.offset = -// Offset(0, max(0, min(-headerTileOffset, height - headerTileHeight))); -// -// } -// -// double getHeaderTileStuckOffset() { -// final scrollableContent = _scrollable.context.findRenderObject(); -// if (scrollableContent.attached) { -// try { -// return localToGlobal(Offset.zero, ancestor: scrollableContent).dy; -// // ignore: avoid_catches_without_on_clauses -// } catch (e) { -// print(e); -// } -// } -// return 0; -// } -// -// -// @override -// void setupParentData(RenderObject child) { -// super.setupParentData(child); -// child.parentData = MultiChildLayoutParentData(); -// } -// -// @override -// void paint(PaintingContext context, Offset offset) { -// defaultPaint(context, offset); -// } -// } diff --git a/lib/components/sticky_header/gf_sticky_header_builder.dart b/lib/components/sticky_header/gf_sticky_header_builder.dart new file mode 100644 index 00000000..94ff2951 --- /dev/null +++ b/lib/components/sticky_header/gf_sticky_header_builder.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; +import 'package:getwidget/getwidget.dart'; + +typedef Widget StickyHeaderWidgetBuilder( + BuildContext context, double stuckAmount); + +/// Sticky Header Builder Widget. +/// +/// The same as [StickyHeader] 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 GFStickyHeaderBuilder extends StatefulWidget { + /// Constructs a new [GFStickyHeaderBuilder] widget. + const GFStickyHeaderBuilder({ + Key key, + @required this.stickyContentBuilder, + @required this.content, + this.direction = Axis.vertical, + this.enableHeaderOverlap = false, + this.callback, + this.stickyContentPosition = GFPosition.start, + }) : assert(direction != null), + super(key: key); + + /// widget can be used to define [stickyContentBuilder]. + final StickyHeaderWidgetBuilder stickyContentBuilder; + + + /// widget can be used to define [content]. + final Widget content; + + /// On state true, the [stickyContent] will overlap the [content]. + /// Default value is false. + final bool enableHeaderOverlap; + + /// [GFPosition] allows to [stickyContentPosition] to stick at top in [Axis.vertical] and stick at start in [Axis.horizontal] + /// Defaults to [GFPosition.start] + final GFPosition stickyContentPosition; + + /// Optional callback with stickyness value. If you think you need this, then you might want to + /// consider using [StickyHeaderBuilder] instead. + final RenderGFStickyHeaderCallback callback; + + /// [direction] allows children to align in vertical / horizontal way + /// Defaults to [Axis.vertical] + final Axis direction; + + + @override + _GFStickyHeaderBuilderState createState() => _GFStickyHeaderBuilderState(); +} + +class _GFStickyHeaderBuilderState extends State { + double _stuckAmount; + + @override + Widget build(BuildContext context) => GFStickyHeader( + enableHeaderOverlap: widget.enableHeaderOverlap, + direction: widget.direction, + stickyContentPosition: widget.stickyContentPosition, + stickyContent: LayoutBuilder( + builder: (context, _) => widget.stickyContentBuilder(context, _stuckAmount ?? 0.0), + ), + content: widget.content, + callback: (double stuckAmount) { + if (_stuckAmount != stuckAmount) { + _stuckAmount = stuckAmount; + WidgetsBinding.instance.endOfFrame.then((_) { + if (mounted) { + setState(() {}); + } + }); + } + }, + ); +} diff --git a/lib/components/sticky_header/render_gf_sticky_header.dart b/lib/components/sticky_header/render_gf_sticky_header.dart new file mode 100644 index 00000000..bf11896d --- /dev/null +++ b/lib/components/sticky_header/render_gf_sticky_header.dart @@ -0,0 +1,260 @@ +import 'dart:math' show min, max; +import 'dart:math' as math; +import 'dart:ui'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; +import 'package:getwidget/getwidget.dart'; + +typedef RenderGFStickyHeaderCallback = void Function(double stuckAmount); + +class RenderGFStickyHeader extends RenderBox with ContainerRenderObjectMixin, + RenderBoxContainerDefaultsMixin, + DebugOverflowIndicatorMixin { + + RenderGFStickyHeader({ + List children, + Axis direction = Axis.horizontal, + bool enableHeaderOverlap = false, + @required ScrollableState scrollable, + RenderGFStickyHeaderCallback callback, + GFPosition stickyContentPosition, + }) : assert(direction != null), + assert(scrollable != null), + _scrollable = scrollable, + _direction = direction, + _callback = callback, + _stickyContentPosition = stickyContentPosition, + _enableHeaderOverlap = enableHeaderOverlap { + addAll(children); + } + + RenderGFStickyHeaderCallback _callback; + final ScrollableState _scrollable; + final bool _enableHeaderOverlap; + final GFPosition _stickyContentPosition; + double _overflow; + + Axis get direction => _direction; + Axis _direction; + set direction(Axis value) { + assert(value != null); + if (_direction != value) { + _direction = value; + markNeedsLayout(); + } + } + + // ignore: avoid_setters_without_getters + set callback(RenderGFStickyHeaderCallback newValue) { + if (_callback == newValue) { + return; + } + _callback = newValue; + markNeedsLayout(); + } + + @override + void setupParentData(RenderBox child) { + if (child.parentData is! FlexParentData) { + child.parentData = FlexParentData(); + } + } + + @override + double computeDistanceToActualBaseline(TextBaseline baseline) { + if (_direction == Axis.horizontal) { + return defaultComputeDistanceToHighestActualBaseline(baseline); + } + return defaultComputeDistanceToFirstActualBaseline(baseline); + } + + int _getFlex(RenderBox child) { + final FlexParentData childParentData = child.parentData; + return childParentData.flex ?? 0; + } + + double _getCrossSize(RenderBox child) { + switch (_direction) { + case Axis.horizontal: + return child.size.height; + case Axis.vertical: + return child.size.width; + } + // ignore: avoid_returning_null + return null; + } + + double _getMainSize(RenderBox child) { + switch (_direction) { + case Axis.horizontal: + return child.size.width; + case Axis.vertical: + return child.size.height; + } + // ignore: avoid_returning_null + return null; + } + + @override + void attach(PipelineOwner owner) { + super.attach(owner); + _scrollable.position.addListener(markNeedsLayout); + } + + @override + void detach() { + _scrollable.position.removeListener(markNeedsLayout); + super.detach(); + } + + RenderBox get _stickyContentBody => lastChild; + RenderBox get _contentBody => firstChild; + + double getHeaderTileStuckOffset() { + final scrollableContent = _scrollable.context.findRenderObject(); + if (scrollableContent.attached) { + try { + return localToGlobal(Offset.zero, ancestor: scrollableContent).dy; + // ignore: avoid_catches_without_on_clauses + } catch (e) { + print(e); + } + } + return 0; + } + + @override + void performLayout() { + assert(childCount == 2); + _stickyContentBody.layout(constraints.loosen(), parentUsesSize: true); + _contentBody.layout(constraints.loosen(), parentUsesSize: true); + + final stickyContentBodyHeight = _stickyContentBody.size.height; + final contentBodyHeight = _contentBody.size.height; + + final height = max( + constraints.minHeight, + _enableHeaderOverlap + ? contentBodyHeight + : stickyContentBodyHeight + contentBodyHeight); + final width = max(constraints.minWidth, contentBodyHeight); + + size = Size( + constraints.constrainWidth(width), constraints.constrainHeight(height)); + + final double stickyContentBodyOffset = getHeaderTileStuckOffset(); + + int totalFlex = 0; + int totalChildren = 0; + assert(constraints != null); + final double maxMainSize = _direction == Axis.horizontal ? constraints.maxWidth : constraints.maxHeight; + final bool canFlex = maxMainSize < double.infinity; + + double crossSize = 0; + double allocatedSize = 0; + RenderBox child = firstChild; + RenderBox lastFlexChild; + while (child != null) { + // ignore: avoid_as + final FlexParentData childParentData = child.parentData as FlexParentData; + totalChildren++; + final int flex = _getFlex(child); + if (flex > 0) { + totalFlex += childParentData.flex; + lastFlexChild = child; + } else { + BoxConstraints innerConstraints; + switch (_direction) { + case Axis.horizontal: + innerConstraints = BoxConstraints(maxHeight: constraints.maxHeight); + break; + case Axis.vertical: + innerConstraints = BoxConstraints(maxWidth: constraints.maxWidth); + break; + } + child.layout(innerConstraints, parentUsesSize: true); + allocatedSize += _getMainSize(child); + crossSize = math.max(crossSize, _getCrossSize(child)); + } + assert(child.parentData == childParentData); + child = childParentData.nextSibling; + } + + final double idealSize = allocatedSize; + double actualSize; + double actualSizeDelta; + switch (_direction) { + case Axis.horizontal: + size = constraints.constrain(Size(idealSize, crossSize)); + actualSize = size.width; + crossSize = size.height; + break; + case Axis.vertical: + size = constraints.constrain(Size(crossSize, idealSize)); + actualSize = size.height; + crossSize = size.width; + break; + } + actualSizeDelta = actualSize - allocatedSize; + _overflow = math.max(0, -actualSizeDelta); + final double remainingSpace = math.max(0, actualSizeDelta); + const double leadingSpace = 0; + const double betweenSpace = 0; + const bool flipMainAxis = !true; + double childMainPosition = flipMainAxis ? actualSize - leadingSpace : leadingSpace; + child = _contentBody; + // ignore: invariant_booleans + while (child != null) { + // ignore: avoid_as + final FlexParentData childParentData = child.parentData as FlexParentData; + + if (flipMainAxis) { + childMainPosition = _getMainSize(child); + } + switch (_direction) { + case Axis.horizontal: + final FlexParentData contentBodyParentData = + _contentBody.parentData; + contentBodyParentData.offset = + const Offset(0,0); + final FlexParentData stickyContentBodyParentData = + _stickyContentBody.parentData; + stickyContentBodyParentData.offset = + Offset(childMainPosition, max(min(-stickyContentBodyOffset, height - stickyContentBodyHeight), 0)); + break; + case Axis.vertical: + final FlexParentData contentBodyParentData = + _contentBody.parentData; + contentBodyParentData.offset = + Offset(0, _enableHeaderOverlap ? 0.0 : stickyContentBodyHeight); + final FlexParentData stickyContentBodyParentData = + _stickyContentBody.parentData; + stickyContentBodyParentData.offset = + Offset(0, max(0, min(-stickyContentBodyOffset, height - stickyContentBodyHeight))); + break; + } + if (_callback != null) { + final stuckAmount = max(min(stickyContentBodyHeight, stickyContentBodyOffset), - stickyContentBodyHeight) / stickyContentBodyHeight; + _callback(stuckAmount); + } + if (flipMainAxis) { + childMainPosition -= betweenSpace; + } else { + childMainPosition += _getMainSize(child) + betweenSpace; + } + child = childParentData.nextSibling; + } + } + + @override + bool hitTestChildren(BoxHitTestResult result, { Offset position }) => defaultHitTestChildren(result, position: position); + + @override + bool get isRepaintBoundary => true; + + @override + void paint(PaintingContext context, Offset offset) { + defaultPaint(context, offset); + } +} \ No newline at end of file diff --git a/lib/getwidget.dart b/lib/getwidget.dart index 207aa2b3..c73500d2 100644 --- a/lib/getwidget.dart +++ b/lib/getwidget.dart @@ -32,6 +32,9 @@ export 'package:getwidget/components/rating/gf_rating.dart'; export 'package:getwidget/components/search_bar/gf_search_bar.dart'; export 'package:getwidget/components/shimmer/gf_shimmer.dart'; export 'package:getwidget/components/slidable/gf_slidable.dart'; +export 'package:getwidget/components/sticky_header/gf_sticky_header.dart'; +export 'package:getwidget/components/sticky_header/gf_sticky_header_builder.dart'; +export 'package:getwidget/components/sticky_header/render_gf_sticky_header.dart'; export 'package:getwidget/components/tabs/gf_segment_tabs.dart'; export 'package:getwidget/components/tabs/gf_tabbar.dart'; export 'package:getwidget/components/tabs/gf_tabbar_view.dart'; @@ -39,7 +42,6 @@ export 'package:getwidget/components/tabs/gf_tabs.dart'; export 'package:getwidget/components/toast/gf_toast.dart'; export 'package:getwidget/components/toggle/gf_toggle.dart'; export 'package:getwidget/components/typography/gf_typography.dart'; -export 'package:getwidget/components/sticky_header/gf_sticky_header.dart'; export 'colors/gf_color.dart'; export 'direction/gf_shimmer_direction.dart'; From 2406a45a4ddbc6f0c41f5ea515a3ea31ebb1a059 Mon Sep 17 00:00:00 2001 From: deepikahr Date: Wed, 23 Sep 2020 20:26:43 +0530 Subject: [PATCH 07/13] added render gf sticky header --- example/lib/main_temp.dart | 2 +- .../sticky_header/gf_sticky_header.dart | 5 +++-- .../sticky_header/gf_sticky_header_builder.dart | 15 +++++---------- .../sticky_header/render_gf_sticky_header.dart | 4 ++-- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/example/lib/main_temp.dart b/example/lib/main_temp.dart index 853c0308..906ae8ca 100644 --- a/example/lib/main_temp.dart +++ b/example/lib/main_temp.dart @@ -122,7 +122,7 @@ class _MyHomePageState extends State itemBuilder: (context, index) => GFStickyHeaderBuilder( direction: Axis.horizontal, // enableHeaderOverlap: true, - stickyContentPosition: GFPosition.start, + stickyContentPosition: GFPosition.end, stickyContentBuilder: (BuildContext context, double stuckAmount) { stuckAmount = 1.0 - stuckAmount.clamp(0.0, 1.0); return Container( diff --git a/lib/components/sticky_header/gf_sticky_header.dart b/lib/components/sticky_header/gf_sticky_header.dart index 586783b9..1c784d80 100644 --- a/lib/components/sticky_header/gf_sticky_header.dart +++ b/lib/components/sticky_header/gf_sticky_header.dart @@ -5,6 +5,8 @@ import 'package:flutter/widgets.dart'; import 'package:meta/meta.dart'; import 'package:getwidget/getwidget.dart'; +/// Place this widget inside a [ListView], [GridView], [CustomScrollView], [SingleChildScrollView] or similar. + class GFStickyHeader extends MultiChildRenderObjectWidget { GFStickyHeader({ Key key, @@ -33,8 +35,7 @@ class GFStickyHeader extends MultiChildRenderObjectWidget { /// Defaults to [GFPosition.start] final GFPosition stickyContentPosition; - /// Optional callback with stickyness value. If you think you need this, then you might want to - /// consider using [StickyHeaderBuilder] instead. + /// Allows to add custom stickyHeader stuck offset value final RenderGFStickyHeaderCallback callback; /// [direction] allows children to align in vertical / horizontal way diff --git a/lib/components/sticky_header/gf_sticky_header_builder.dart b/lib/components/sticky_header/gf_sticky_header_builder.dart index 94ff2951..54ca4892 100644 --- a/lib/components/sticky_header/gf_sticky_header_builder.dart +++ b/lib/components/sticky_header/gf_sticky_header_builder.dart @@ -1,16 +1,12 @@ import 'package:flutter/material.dart'; import 'package:getwidget/getwidget.dart'; -typedef Widget StickyHeaderWidgetBuilder( +typedef StickyHeaderWidgetBuilder = Widget Function( BuildContext context, double stuckAmount); -/// Sticky Header Builder Widget. -/// -/// The same as [StickyHeader] 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 GFStickyHeaderBuilder extends StatefulWidget { /// Constructs a new [GFStickyHeaderBuilder] widget. const GFStickyHeaderBuilder({ @@ -31,7 +27,7 @@ class GFStickyHeaderBuilder extends StatefulWidget { /// widget can be used to define [content]. final Widget content; - /// On state true, the [stickyContent] will overlap the [content]. + /// On state true, the [stickyContentBuilder] will overlap the [content]. /// Default value is false. final bool enableHeaderOverlap; @@ -39,8 +35,7 @@ class GFStickyHeaderBuilder extends StatefulWidget { /// Defaults to [GFPosition.start] final GFPosition stickyContentPosition; - /// Optional callback with stickyness value. If you think you need this, then you might want to - /// consider using [StickyHeaderBuilder] instead. + /// Allows to add custom stickyHeader stuck offset value final RenderGFStickyHeaderCallback callback; /// [direction] allows children to align in vertical / horizontal way diff --git a/lib/components/sticky_header/render_gf_sticky_header.dart b/lib/components/sticky_header/render_gf_sticky_header.dart index bf11896d..a904d30d 100644 --- a/lib/components/sticky_header/render_gf_sticky_header.dart +++ b/lib/components/sticky_header/render_gf_sticky_header.dart @@ -33,7 +33,7 @@ class RenderGFStickyHeader extends RenderBox with ContainerRenderObjectMixin _direction; Axis _direction; @@ -197,7 +197,7 @@ class RenderGFStickyHeader extends RenderBox with ContainerRenderObjectMixin Date: Thu, 24 Sep 2020 17:29:33 +0530 Subject: [PATCH 08/13] stickyContentPosition feature added --- example/lib/main_temp.dart | 216 ++++++++++-------- .../sticky_header/gf_sticky_header.dart | 34 ++- .../gf_sticky_header_builder.dart | 16 +- .../render_gf_sticky_header.dart | 58 +++-- 4 files changed, 177 insertions(+), 147 deletions(-) diff --git a/example/lib/main_temp.dart b/example/lib/main_temp.dart index 906ae8ca..b502ba6d 100644 --- a/example/lib/main_temp.dart +++ b/example/lib/main_temp.dart @@ -81,35 +81,6 @@ class _MyHomePageState extends State 'Xamarin2', ]; - void _persistentBottomSheet() { - _scaffoldKey.currentState.showBottomSheet((context) => Container( - color: Colors.redAccent, - height: 250, - child: const Center( - child: Text('Hey! guys , this is a persistent bottom sheet'), - ), - )); - } - - void _modalBottomSheetMenu() { - showModalBottomSheet( - context: context, - elevation: 10, - builder: (builder) => Container( - height: 350, - color: Colors.transparent, - child: Container( - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10), - topRight: Radius.circular(10))), - child: const Center( - child: Text('This is a modal sheet'), - )), - )); - } - @override Widget build(BuildContext context) => Scaffold( appBar: GFAppBar( @@ -117,84 +88,133 @@ class _MyHomePageState extends State ), body: - ListView.builder( + // ListView.builder( + // itemCount: imageList.length, + // itemBuilder: (context, index) => GFStickyHeaderBuilder( + // direction: Axis.horizontal, + // // enableHeaderOverlap: true, + // // stickyContentPosition: GFPosition.end, + // stickyContentBuilder: + // (BuildContext context, double stuckValue) { + // stuckValue = 1.0 - stuckValue.clamp(0.0, 1.0); + // return Container( + // height: 50, + // width: MediaQuery.of(context).size.width * 0.5, + // color: Color.lerp(Colors.teal[100], Colors.teal[600], stuckValue), + // padding: const EdgeInsets.symmetric(horizontal: 16), + // alignment: Alignment.centerLeft, + // child: Row( + // children: [ + // Expanded( + // child: Text( + // 'Image #$index', + // style: const TextStyle(color: Colors.white), + // ), + // ), + // Offstage( + // offstage: stuckValue <= 0.0, + // child: Opacity( + // opacity: stuckValue, + // child: IconButton( + // icon: const Icon(Icons.image, color: Colors.white), + // onPressed: () => + // Scaffold.of(context).showSnackBar(SnackBar(content: Text('Favorite #$index'))), + // ), + // ), + // ), + // ], + // ), + // ); + // }, + // content: Container( + // // margin: EdgeInsets.all(8), + // height: 200, + // width: MediaQuery.of(context).size.width * 0.5, + // color: Colors.teal, + // child: Image.network(imageList[index], + // fit: BoxFit.cover, width: 100, height: 200), + // ), + // )) + // ); - itemBuilder: (context, index) => GFStickyHeaderBuilder( - direction: Axis.horizontal, - // enableHeaderOverlap: true, - stickyContentPosition: GFPosition.end, - stickyContentBuilder: (BuildContext context, double stuckAmount) { - stuckAmount = 1.0 - stuckAmount.clamp(0.0, 1.0); - return Container( - height: 50.0, - width: 200, - color: - Colors.grey[900].withOpacity(0.6 + stuckAmount * 0.4), - padding: EdgeInsets.symmetric(horizontal: 16.0), - alignment: Alignment.centerLeft, - child: Text( - 'Header #$index', - style: const TextStyle(color: Colors.white), - ), - ); - }, - content: Container( - // margin: EdgeInsets.all(8), - height: 200, - width: 200, - color: Colors.teal, - child: Image.network(imageList[index], - fit: BoxFit.cover, - width: 100, - height: 200.0 - ), + // Center( + // // alignment: AlignmentDirectional.center, + // child: ListView.builder( + // itemCount: imageList.length, + // itemBuilder: (context, index) => GFStickyHeaderBuilder( + // direction: Axis.vertical, + // // enableHeaderOverlap: true, + // // stickyContentPosition: GFPosition.end, + // stickyContentBuilder: + // (BuildContext context, double stuckValue) { + // stuckValue = 1.0 - stuckValue.clamp(0.0, 1.0); + // return Center( + // child: Container( + // height: 50.0, + // width: 100, + // color: + // Colors.grey[900].withOpacity(0.6 + stuckValue * 0.4), + // padding: EdgeInsets.symmetric(horizontal: 16.0), + // alignment: Alignment.centerLeft, + // child: Text( + // 'Header #$index', + // style: const TextStyle(color: Colors.white), + // ), + // ), + // ); + // }, + // content: Container( + // // margin: EdgeInsets.all(8), + // height: 200, + // width: 200, + // color: Colors.teal, + // child: Image.network(imageList[index], + // fit: BoxFit.cover, width: 100, height: 200.0), + // ), + // ))), + // ); + + Container( + height: 600, + color: Colors.teal, + child: ListView.builder( + itemCount: imageList.length, + itemBuilder: (context, index) => GFStickyHeader( + // enableHeaderOverlap: true, + direction: Axis.vertical, + // stickyContentPosition: GFPosition.end, + stickyContent: Container( + // alignment: AlignmentDirectional.centerEnd, + child: Container( + alignment: AlignmentDirectional.center, + height: 50, + width: 100, + color: Colors.blueGrey[700], + padding: EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + 'Image $index', + style: const TextStyle(color: Colors.white), ), - ) - ) + ), + ), + content: Container( + height: 100, + width: 200, + child: Image.network(imageList[index], + fit: BoxFit.cover, + ), + ), + ) + ), + ) ); - // Container( - // height: 600, - // color: Colors.teal, - // child: ListView.builder( - // itemCount: imageList.length, - // itemBuilder: (context, index) => GFStickyHeader( - // // enableHeaderOverlap: true, - // direction: Axis.vertical, - // stickyContentPosition: GFPosition.end, - // stickyContent: Container( - // // alignment: AlignmentDirectional.centerEnd, - // child: Container( - // alignment: AlignmentDirectional.center, - // height: 50, - // width: 100, - // color: Colors.blueGrey[700], - // padding: EdgeInsets.symmetric(horizontal: 16.0), - // child: Text( - // 'H $index', - // style: const TextStyle(color: Colors.white), - // ), - // ), - // ), - // content: Container( - // height: 100, - // width: 200, - // child: Image.network(imageList[index], - // fit: BoxFit.cover, - // // width: 100, - // // height: 200.0 - // ), - // ), - // ) - // ), - // ) - // ); - // ListView( // children: [ // Image.network(imageList[3], height: 200, fit: BoxFit.cover,), // Image.network(imageList[4], height: 200, fit: BoxFit.cover,), // GFStickyHeader( +// enableHeaderOverlap: true, // stickyContent: Container( // height: 100, // color: Colors.teal, diff --git a/lib/components/sticky_header/gf_sticky_header.dart b/lib/components/sticky_header/gf_sticky_header.dart index 1c784d80..6cabca89 100644 --- a/lib/components/sticky_header/gf_sticky_header.dart +++ b/lib/components/sticky_header/gf_sticky_header.dart @@ -8,18 +8,21 @@ import 'package:getwidget/getwidget.dart'; /// Place this widget inside a [ListView], [GridView], [CustomScrollView], [SingleChildScrollView] or similar. class GFStickyHeader extends MultiChildRenderObjectWidget { - GFStickyHeader({ - Key key, - @required this.stickyContent, - @required this.content, - this.direction = Axis.vertical, - this.enableHeaderOverlap = false, - this.callback, - this.stickyContentPosition = GFPosition.start - }) : assert(direction != null), - super(key: key, children: stickyContentPosition == GFPosition.start ? [stickyContent, content] : [content, stickyContent] - ); - + GFStickyHeader( + {Key key, + @required this.stickyContent, + @required this.content, + this.direction = Axis.vertical, + this.enableHeaderOverlap = false, + this.callback, + this.stickyContentPosition = GFPosition.start}) + : assert(direction != null), + super( + key: key, + children: stickyContentPosition == GFPosition.start && direction == Axis.horizontal + ? [stickyContent, content] + : stickyContentPosition == GFPosition.start && direction == Axis.vertical ? [content, stickyContent] + : [content, stickyContent]); /// widget can be used to define [stickyContent]. final Widget stickyContent; @@ -42,7 +45,6 @@ class GFStickyHeader extends MultiChildRenderObjectWidget { /// Defaults to [Axis.vertical] final Axis direction; - @override RenderGFStickyHeader createRenderObject(BuildContext context) { final scrollable = Scrollable.of(context); @@ -52,11 +54,7 @@ class GFStickyHeader extends MultiChildRenderObjectWidget { scrollable: scrollable, enableHeaderOverlap: enableHeaderOverlap, callback: callback, - stickyContentPosition: stickyContentPosition, + stickyContentPosition: stickyContentPosition, ); } } - - - - diff --git a/lib/components/sticky_header/gf_sticky_header_builder.dart b/lib/components/sticky_header/gf_sticky_header_builder.dart index 54ca4892..f2bb27c1 100644 --- a/lib/components/sticky_header/gf_sticky_header_builder.dart +++ b/lib/components/sticky_header/gf_sticky_header_builder.dart @@ -2,8 +2,7 @@ import 'package:flutter/material.dart'; import 'package:getwidget/getwidget.dart'; typedef StickyHeaderWidgetBuilder = Widget Function( - BuildContext context, double stuckAmount); - + BuildContext context, double stuckValue); /// Place this widget inside a [ListView], [GridView], [CustomScrollView], [SingleChildScrollView] or similar. @@ -23,7 +22,6 @@ class GFStickyHeaderBuilder extends StatefulWidget { /// widget can be used to define [stickyContentBuilder]. final StickyHeaderWidgetBuilder stickyContentBuilder; - /// widget can be used to define [content]. final Widget content; @@ -42,13 +40,12 @@ class GFStickyHeaderBuilder extends StatefulWidget { /// Defaults to [Axis.vertical] final Axis direction; - @override _GFStickyHeaderBuilderState createState() => _GFStickyHeaderBuilderState(); } class _GFStickyHeaderBuilderState extends State { - double _stuckAmount; + double _stuckValue; @override Widget build(BuildContext context) => GFStickyHeader( @@ -56,12 +53,13 @@ class _GFStickyHeaderBuilderState extends State { direction: widget.direction, stickyContentPosition: widget.stickyContentPosition, stickyContent: LayoutBuilder( - builder: (context, _) => widget.stickyContentBuilder(context, _stuckAmount ?? 0.0), + builder: (context, _) => + widget.stickyContentBuilder(context, _stuckValue ?? 0.0), ), content: widget.content, - callback: (double stuckAmount) { - if (_stuckAmount != stuckAmount) { - _stuckAmount = stuckAmount; + callback: (double stuckValue) { + if (_stuckValue != stuckValue) { + _stuckValue = stuckValue; WidgetsBinding.instance.endOfFrame.then((_) { if (mounted) { setState(() {}); diff --git a/lib/components/sticky_header/render_gf_sticky_header.dart b/lib/components/sticky_header/render_gf_sticky_header.dart index a904d30d..802e7d77 100644 --- a/lib/components/sticky_header/render_gf_sticky_header.dart +++ b/lib/components/sticky_header/render_gf_sticky_header.dart @@ -6,12 +6,13 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:getwidget/getwidget.dart'; -typedef RenderGFStickyHeaderCallback = void Function(double stuckAmount); - -class RenderGFStickyHeader extends RenderBox with ContainerRenderObjectMixin, - RenderBoxContainerDefaultsMixin, - DebugOverflowIndicatorMixin { +typedef RenderGFStickyHeaderCallback = void Function(double stuckValue); +class RenderGFStickyHeader extends RenderBox + with + ContainerRenderObjectMixin, + RenderBoxContainerDefaultsMixin, + DebugOverflowIndicatorMixin { RenderGFStickyHeader({ List children, Axis direction = Axis.horizontal, @@ -19,7 +20,7 @@ class RenderGFStickyHeader extends RenderBox with ContainerRenderObjectMixin lastChild; - RenderBox get _contentBody => firstChild; + RenderBox get _stickyContentBody => + _stickyContentPosition == GFPosition.start && _direction == Axis.horizontal ? firstChild + : _stickyContentPosition == GFPosition.end && _direction == Axis.vertical ? firstChild: lastChild; + RenderBox get _contentBody => + _stickyContentPosition == GFPosition.start && _direction == Axis.horizontal ? lastChild + : _stickyContentPosition == GFPosition.end && _direction == Axis.vertical ? lastChild: firstChild; double getHeaderTileStuckOffset() { final scrollableContent = _scrollable.context.findRenderObject(); @@ -148,7 +153,9 @@ class RenderGFStickyHeader extends RenderBox with ContainerRenderObjectMixin defaultHitTestChildren(result, position: position); + bool hitTestChildren(BoxHitTestResult result, {Offset position}) => + defaultHitTestChildren(result, position: position); @override bool get isRepaintBoundary => true; @@ -257,4 +271,4 @@ class RenderGFStickyHeader extends RenderBox with ContainerRenderObjectMixin Date: Thu, 24 Sep 2020 18:01:38 +0530 Subject: [PATCH 09/13] analyze issues fixed --- example/lib/main_temp.dart | 6 +++--- .../sticky_header/render_gf_sticky_header.dart | 16 +++------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/example/lib/main_temp.dart b/example/lib/main_temp.dart index b502ba6d..6f9c4b8c 100644 --- a/example/lib/main_temp.dart +++ b/example/lib/main_temp.dart @@ -180,7 +180,7 @@ class _MyHomePageState extends State child: ListView.builder( itemCount: imageList.length, itemBuilder: (context, index) => GFStickyHeader( - // enableHeaderOverlap: true, + enableHeaderOverlap: true, direction: Axis.vertical, // stickyContentPosition: GFPosition.end, stickyContent: Container( @@ -198,10 +198,10 @@ class _MyHomePageState extends State ), ), content: Container( - height: 100, + // height: 100, width: 200, child: Image.network(imageList[index], - fit: BoxFit.cover, + fit: BoxFit.cover, height: 200, ), ), ) diff --git a/lib/components/sticky_header/render_gf_sticky_header.dart b/lib/components/sticky_header/render_gf_sticky_header.dart index 802e7d77..b6492593 100644 --- a/lib/components/sticky_header/render_gf_sticky_header.dart +++ b/lib/components/sticky_header/render_gf_sticky_header.dart @@ -150,26 +150,20 @@ class RenderGFStickyHeader extends RenderBox final double stickyContentBodyOffset = getHeaderTileStuckOffset(); - int totalFlex = 0; - int totalChildren = 0; + assert(constraints != null); - final double maxMainSize = _direction == Axis.horizontal - ? constraints.maxWidth - : constraints.maxHeight; - final bool canFlex = maxMainSize < double.infinity; double crossSize = 0; double allocatedSize = 0; RenderBox child = firstChild; - RenderBox lastFlexChild; + // ignore: unused_local_variable + int totalChildren = 0; while (child != null) { // ignore: avoid_as final FlexParentData childParentData = child.parentData as FlexParentData; totalChildren++; final int flex = _getFlex(child); if (flex > 0) { - totalFlex += childParentData.flex; - lastFlexChild = child; } else { BoxConstraints innerConstraints; switch (_direction) { @@ -190,7 +184,6 @@ class RenderGFStickyHeader extends RenderBox final double idealSize = allocatedSize; double actualSize; - double actualSizeDelta; switch (_direction) { case Axis.horizontal: size = constraints.constrain(Size(idealSize, crossSize)); @@ -203,9 +196,6 @@ class RenderGFStickyHeader extends RenderBox crossSize = size.width; break; } - actualSizeDelta = actualSize - allocatedSize; - // _overflow = math.max(0, -actualSizeDelta); - final double remainingSpace = math.max(0, actualSizeDelta); const double leadingSpace = 0; const double betweenSpace = 0; const bool flipMainAxis = !true; From 01cf2c817a4b8fa98395bee827cad66f9aae004a Mon Sep 17 00:00:00 2001 From: deepikahr Date: Thu, 24 Sep 2020 18:12:55 +0530 Subject: [PATCH 10/13] added comments --- example/lib/main_temp.dart | 4 ++-- lib/components/sticky_header/render_gf_sticky_header.dart | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/example/lib/main_temp.dart b/example/lib/main_temp.dart index 6f9c4b8c..8cdc746e 100644 --- a/example/lib/main_temp.dart +++ b/example/lib/main_temp.dart @@ -182,7 +182,7 @@ class _MyHomePageState extends State itemBuilder: (context, index) => GFStickyHeader( enableHeaderOverlap: true, direction: Axis.vertical, - // stickyContentPosition: GFPosition.end, + stickyContentPosition: GFPosition.end, stickyContent: Container( // alignment: AlignmentDirectional.centerEnd, child: Container( @@ -201,7 +201,7 @@ class _MyHomePageState extends State // height: 100, width: 200, child: Image.network(imageList[index], - fit: BoxFit.cover, height: 200, + fit: BoxFit.cover, height: 100, ), ), ) diff --git a/lib/components/sticky_header/render_gf_sticky_header.dart b/lib/components/sticky_header/render_gf_sticky_header.dart index b6492593..ffc7a30a 100644 --- a/lib/components/sticky_header/render_gf_sticky_header.dart +++ b/lib/components/sticky_header/render_gf_sticky_header.dart @@ -34,7 +34,6 @@ class RenderGFStickyHeader extends RenderBox final ScrollableState _scrollable; final bool _enableHeaderOverlap; final GFPosition _stickyContentPosition; - // double _overflow; Axis get direction => _direction; Axis _direction; From c5da8b528c41d09f8947f04c1e7ad633c7c1ebef Mon Sep 17 00:00:00 2001 From: deepikahr Date: Thu, 24 Sep 2020 19:06:25 +0530 Subject: [PATCH 11/13] changes done --- example/lib/main_temp.dart | 236 +++++++++--------- .../sticky_header/gf_sticky_header.dart | 11 +- .../gf_sticky_header_builder.dart | 2 +- .../render_gf_sticky_header.dart | 52 ++-- 4 files changed, 154 insertions(+), 147 deletions(-) diff --git a/example/lib/main_temp.dart b/example/lib/main_temp.dart index 8cdc746e..1b1ea60e 100644 --- a/example/lib/main_temp.dart +++ b/example/lib/main_temp.dart @@ -88,126 +88,126 @@ class _MyHomePageState extends State ), body: - // ListView.builder( - // itemCount: imageList.length, - // itemBuilder: (context, index) => GFStickyHeaderBuilder( - // direction: Axis.horizontal, - // // enableHeaderOverlap: true, - // // stickyContentPosition: GFPosition.end, - // stickyContentBuilder: - // (BuildContext context, double stuckValue) { - // stuckValue = 1.0 - stuckValue.clamp(0.0, 1.0); - // return Container( - // height: 50, - // width: MediaQuery.of(context).size.width * 0.5, - // color: Color.lerp(Colors.teal[100], Colors.teal[600], stuckValue), - // padding: const EdgeInsets.symmetric(horizontal: 16), - // alignment: Alignment.centerLeft, - // child: Row( - // children: [ - // Expanded( - // child: Text( - // 'Image #$index', - // style: const TextStyle(color: Colors.white), - // ), - // ), - // Offstage( - // offstage: stuckValue <= 0.0, - // child: Opacity( - // opacity: stuckValue, - // child: IconButton( - // icon: const Icon(Icons.image, color: Colors.white), - // onPressed: () => - // Scaffold.of(context).showSnackBar(SnackBar(content: Text('Favorite #$index'))), - // ), - // ), - // ), - // ], - // ), - // ); - // }, - // content: Container( - // // margin: EdgeInsets.all(8), - // height: 200, - // width: MediaQuery.of(context).size.width * 0.5, - // color: Colors.teal, - // child: Image.network(imageList[index], - // fit: BoxFit.cover, width: 100, height: 200), - // ), - // )) - // ); + // ListView.builder( + // itemCount: imageList.length, + // itemBuilder: (context, index) => GFStickyHeaderBuilder( + // direction: Axis.horizontal, + // // enableHeaderOverlap: true, + // // stickyContentPosition: GFPosition.end, + // stickyContentBuilder: + // (BuildContext context, double stuckValue) { + // stuckValue = 1.0 - stuckValue.clamp(0.0, 1.0); + // return Container( + // height: 50, + // width: MediaQuery.of(context).size.width * 0.5, + // color: Color.lerp(Colors.teal[100], Colors.teal[600], stuckValue), + // padding: const EdgeInsets.symmetric(horizontal: 16), + // alignment: Alignment.centerLeft, + // child: Row( + // children: [ + // Expanded( + // child: Text( + // 'Image #$index', + // style: const TextStyle(color: Colors.white), + // ), + // ), + // Offstage( + // offstage: stuckValue <= 0.0, + // child: Opacity( + // opacity: stuckValue, + // child: IconButton( + // icon: const Icon(Icons.image, color: Colors.white), + // onPressed: () => + // Scaffold.of(context).showSnackBar(SnackBar(content: Text('Favorite #$index'))), + // ), + // ), + // ), + // ], + // ), + // ); + // }, + // content: Container( + // // margin: EdgeInsets.all(8), + // height: 200, + // width: MediaQuery.of(context).size.width * 0.5, + // color: Colors.teal, + // child: Image.network(imageList[index], + // fit: BoxFit.cover, width: 100, height: 200), + // ), + // )) + // ); - // Center( - // // alignment: AlignmentDirectional.center, - // child: ListView.builder( - // itemCount: imageList.length, - // itemBuilder: (context, index) => GFStickyHeaderBuilder( - // direction: Axis.vertical, - // // enableHeaderOverlap: true, - // // stickyContentPosition: GFPosition.end, - // stickyContentBuilder: - // (BuildContext context, double stuckValue) { - // stuckValue = 1.0 - stuckValue.clamp(0.0, 1.0); - // return Center( - // child: Container( - // height: 50.0, - // width: 100, - // color: - // Colors.grey[900].withOpacity(0.6 + stuckValue * 0.4), - // padding: EdgeInsets.symmetric(horizontal: 16.0), - // alignment: Alignment.centerLeft, - // child: Text( - // 'Header #$index', - // style: const TextStyle(color: Colors.white), - // ), - // ), - // ); - // }, - // content: Container( - // // margin: EdgeInsets.all(8), - // height: 200, - // width: 200, - // color: Colors.teal, - // child: Image.network(imageList[index], - // fit: BoxFit.cover, width: 100, height: 200.0), - // ), - // ))), - // ); + // Center( + // // alignment: AlignmentDirectional.center, + // child: ListView.builder( + // itemCount: imageList.length, + // itemBuilder: (context, index) => GFStickyHeaderBuilder( + // direction: Axis.vertical, + // // enableHeaderOverlap: true, + // // stickyContentPosition: GFPosition.end, + // stickyContentBuilder: + // (BuildContext context, double stuckValue) { + // stuckValue = 1.0 - stuckValue.clamp(0.0, 1.0); + // return Center( + // child: Container( + // height: 50.0, + // width: 100, + // color: + // Colors.grey[900].withOpacity(0.6 + stuckValue * 0.4), + // padding: EdgeInsets.symmetric(horizontal: 16.0), + // alignment: Alignment.centerLeft, + // child: Text( + // 'Header #$index', + // style: const TextStyle(color: Colors.white), + // ), + // ), + // ); + // }, + // content: Container( + // // margin: EdgeInsets.all(8), + // height: 200, + // width: 200, + // color: Colors.teal, + // child: Image.network(imageList[index], + // fit: BoxFit.cover, width: 100, height: 200.0), + // ), + // ))), + // ); - Container( - height: 600, - color: Colors.teal, - child: ListView.builder( - itemCount: imageList.length, - itemBuilder: (context, index) => GFStickyHeader( - enableHeaderOverlap: true, - direction: Axis.vertical, - stickyContentPosition: GFPosition.end, - stickyContent: Container( - // alignment: AlignmentDirectional.centerEnd, - child: Container( - alignment: AlignmentDirectional.center, - height: 50, - width: 100, - color: Colors.blueGrey[700], - padding: EdgeInsets.symmetric(horizontal: 16.0), - child: Text( - 'Image $index', - style: const TextStyle(color: Colors.white), - ), - ), - ), - content: Container( - // height: 100, - width: 200, - child: Image.network(imageList[index], - fit: BoxFit.cover, height: 100, - ), - ), - ) - ), - ) - ); + Container( + height: 600, + color: Colors.teal, + child: ListView.builder( + itemCount: imageList.length, + itemBuilder: (context, index) => GFStickyHeader( + // enableHeaderOverlap: true, + direction: Axis.horizontal, + stickyContentPosition: GFPosition.end, + stickyContent: Container( + // alignment: AlignmentDirectional.centerEnd, + child: Container( + alignment: AlignmentDirectional.center, + height: 50, + width: 100, + color: Colors.blueGrey[700], + padding: EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + 'Image $index', + style: const TextStyle(color: Colors.white), + ), + ), + ), + content: Container( + // height: 100, + width: 200, + child: Image.network( + imageList[index], + fit: BoxFit.cover, + height: 100, + ), + ), + )), + )); // ListView( // children: [ diff --git a/lib/components/sticky_header/gf_sticky_header.dart b/lib/components/sticky_header/gf_sticky_header.dart index 6cabca89..a18e2008 100644 --- a/lib/components/sticky_header/gf_sticky_header.dart +++ b/lib/components/sticky_header/gf_sticky_header.dart @@ -19,10 +19,13 @@ class GFStickyHeader extends MultiChildRenderObjectWidget { : assert(direction != null), super( key: key, - children: stickyContentPosition == GFPosition.start && direction == Axis.horizontal + children: stickyContentPosition == GFPosition.start && + direction == Axis.horizontal ? [stickyContent, content] - : stickyContentPosition == GFPosition.start && direction == Axis.vertical ? [content, stickyContent] - : [content, stickyContent]); + : stickyContentPosition == GFPosition.start && + direction == Axis.vertical + ? [content, stickyContent] + : [content, stickyContent]); /// widget can be used to define [stickyContent]. final Widget stickyContent; @@ -31,7 +34,7 @@ class GFStickyHeader extends MultiChildRenderObjectWidget { final Widget content; /// On state true, the [stickyContent] will overlap the [content]. - /// Default value is false. + /// Only works when direction is [Axis.vertical]. Default set to false. final bool enableHeaderOverlap; /// [GFPosition] allows to [stickyContentPosition] to stick at top in [Axis.vertical] and stick at start in [Axis.horizontal] diff --git a/lib/components/sticky_header/gf_sticky_header_builder.dart b/lib/components/sticky_header/gf_sticky_header_builder.dart index f2bb27c1..1abb64e0 100644 --- a/lib/components/sticky_header/gf_sticky_header_builder.dart +++ b/lib/components/sticky_header/gf_sticky_header_builder.dart @@ -26,7 +26,7 @@ class GFStickyHeaderBuilder extends StatefulWidget { final Widget content; /// On state true, the [stickyContentBuilder] will overlap the [content]. - /// Default value is false. + /// Only works when direction is [Axis.vertical]. Default set to false. final bool enableHeaderOverlap; /// [GFPosition] allows to [stickyContentPosition] to stick at top in [Axis.vertical] and stick at start in [Axis.horizontal] diff --git a/lib/components/sticky_header/render_gf_sticky_header.dart b/lib/components/sticky_header/render_gf_sticky_header.dart index ffc7a30a..43b36ea0 100644 --- a/lib/components/sticky_header/render_gf_sticky_header.dart +++ b/lib/components/sticky_header/render_gf_sticky_header.dart @@ -46,11 +46,11 @@ class RenderGFStickyHeader extends RenderBox } // ignore: avoid_setters_without_getters - set callback(RenderGFStickyHeaderCallback newValue) { - if (_callback == newValue) { + set callback(RenderGFStickyHeaderCallback value) { + if (_callback == value) { return; } - _callback = newValue; + _callback = value; markNeedsLayout(); } @@ -61,14 +61,6 @@ class RenderGFStickyHeader extends RenderBox } } - @override - double computeDistanceToActualBaseline(TextBaseline baseline) { - if (_direction == Axis.horizontal) { - return defaultComputeDistanceToHighestActualBaseline(baseline); - } - return defaultComputeDistanceToFirstActualBaseline(baseline); - } - int _getFlex(RenderBox child) { final FlexParentData childParentData = child.parentData; return childParentData.flex ?? 0; @@ -108,12 +100,19 @@ class RenderGFStickyHeader extends RenderBox super.detach(); } - RenderBox get _stickyContentBody => - _stickyContentPosition == GFPosition.start && _direction == Axis.horizontal ? firstChild - : _stickyContentPosition == GFPosition.end && _direction == Axis.vertical ? firstChild: lastChild; - RenderBox get _contentBody => - _stickyContentPosition == GFPosition.start && _direction == Axis.horizontal ? lastChild - : _stickyContentPosition == GFPosition.end && _direction == Axis.vertical ? lastChild: firstChild; + RenderBox get _stickyContentBody => _stickyContentPosition == + GFPosition.start && + _direction == Axis.horizontal + ? firstChild + : _stickyContentPosition == GFPosition.end && _direction == Axis.vertical + ? firstChild + : lastChild; + RenderBox get _contentBody => _stickyContentPosition == GFPosition.start && + _direction == Axis.horizontal + ? lastChild + : _stickyContentPosition == GFPosition.end && _direction == Axis.vertical + ? lastChild + : firstChild; double getHeaderTileStuckOffset() { final scrollableContent = _scrollable.context.findRenderObject(); @@ -149,11 +148,10 @@ class RenderGFStickyHeader extends RenderBox final double stickyContentBodyOffset = getHeaderTileStuckOffset(); - assert(constraints != null); double crossSize = 0; - double allocatedSize = 0; + double allotedSize = 0; RenderBox child = firstChild; // ignore: unused_local_variable int totalChildren = 0; @@ -174,14 +172,14 @@ class RenderGFStickyHeader extends RenderBox break; } child.layout(innerConstraints, parentUsesSize: true); - allocatedSize += _getMainSize(child); + allotedSize += _getMainSize(child); crossSize = math.max(crossSize, _getCrossSize(child)); } assert(child.parentData == childParentData); child = childParentData.nextSibling; } - final double idealSize = allocatedSize; + final double idealSize = allotedSize; double actualSize; switch (_direction) { case Axis.horizontal: @@ -213,11 +211,17 @@ class RenderGFStickyHeader extends RenderBox case Axis.horizontal: final FlexParentData contentBodyParentData = _contentBody.parentData; contentBodyParentData.offset = - _stickyContentPosition == GFPosition.start ? Offset(_stickyContentBody.size.width, 0) : const Offset(0, 0); + _stickyContentPosition == GFPosition.start + ? Offset(_stickyContentBody.size.width, 0) + : const Offset(0, 0); final FlexParentData stickyContentBodyParentData = _stickyContentBody.parentData; - stickyContentBodyParentData.offset = - Offset(childMainPosition, max(min(-stickyContentBodyOffset, height - stickyContentBodyHeight), 0)); + stickyContentBodyParentData.offset = Offset( + childMainPosition, + max( + min(-stickyContentBodyOffset, + height - stickyContentBodyHeight), + 0)); break; case Axis.vertical: final FlexParentData contentBodyParentData = _contentBody.parentData; From 94de6d2097abf729bd5a5a03265e42c31b0c4f3f Mon Sep 17 00:00:00 2001 From: deepikahr Date: Thu, 24 Sep 2020 19:24:56 +0530 Subject: [PATCH 12/13] gfStickyHeader component completed --- example/lib/main_temp.dart | 508 ------------------ .../render_gf_sticky_header.dart | 10 +- 2 files changed, 5 insertions(+), 513 deletions(-) delete mode 100644 example/lib/main_temp.dart diff --git a/example/lib/main_temp.dart b/example/lib/main_temp.dart deleted file mode 100644 index 1b1ea60e..00000000 --- a/example/lib/main_temp.dart +++ /dev/null @@ -1,508 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; -import 'package:getwidget/getwidget.dart'; - -final List imageList = [ - 'https://cdn.pixabay.com/photo/2017/12/03/18/04/christmas-balls-2995437_960_720.jpg', - 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', - 'https://cdn.pixabay.com/photo/2019/12/19/10/55/christmas-market-4705877_960_720.jpg', - 'https://cdn.pixabay.com/photo/2019/12/20/00/03/road-4707345_960_720.jpg', - 'https://cdn.pixabay.com/photo/2016/11/22/07/09/spruce-1848543__340.jpg', - 'https://cdn.pixabay.com/photo/2017/12/03/18/04/christmas-balls-2995437_960_720.jpg', - 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', - 'https://cdn.pixabay.com/photo/2017/12/03/18/04/christmas-balls-2995437_960_720.jpg', - 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', - 'https://cdn.pixabay.com/photo/2019/12/19/10/55/christmas-market-4705877_960_720.jpg', - 'https://cdn.pixabay.com/photo/2019/12/20/00/03/road-4707345_960_720.jpg', - 'https://cdn.pixabay.com/photo/2016/11/22/07/09/spruce-1848543__340.jpg', - 'https://cdn.pixabay.com/photo/2017/12/03/18/04/christmas-balls-2995437_960_720.jpg', - 'https://cdn.pixabay.com/photo/2017/12/13/00/23/christmas-3015776_960_720.jpg', -]; - -void main() => runApp(MyApp()); - -class MyApp extends StatelessWidget { - @override - Widget build(BuildContext context) => MaterialApp( - title: 'GetWidget Example', - theme: ThemeData( - primarySwatch: Colors.blue, - ), - debugShowCheckedModeBanner: false, - home: const MyHomePage(title: 'GetWidget Example'), - ); -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({Key key, this.title}) : super(key: key); - - final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State - with SingleTickerProviderStateMixin { - final _scaffoldKey = new GlobalKey(); - TabController tabController; - final _ratingController = TextEditingController(); - bool check = false; - String searchData; - final TextEditingController _searchController = TextEditingController(); - int groupValue = 0; - final GFBottomSheetController _controller = GFBottomSheetController(); - - @override - void initState() { - super.initState(); - _ratingController.text = '4.5'; - tabController = TabController(length: 6, initialIndex: 3, vsync: this); - } - - @override - void dispose() { - tabController.dispose(); - super.dispose(); - } - - bool switchValue = true; - bool showToast = false; - - List list = [ - 'Flutter', - 'React', - 'Ionic', - 'Xamarin', - 'Flutter2', - 'React2', - 'Ionic2', - 'Xamarin2', - ]; - - @override - Widget build(BuildContext context) => Scaffold( - appBar: GFAppBar( - title: const Text('UI KIt'), - ), - body: - - // ListView.builder( - // itemCount: imageList.length, - // itemBuilder: (context, index) => GFStickyHeaderBuilder( - // direction: Axis.horizontal, - // // enableHeaderOverlap: true, - // // stickyContentPosition: GFPosition.end, - // stickyContentBuilder: - // (BuildContext context, double stuckValue) { - // stuckValue = 1.0 - stuckValue.clamp(0.0, 1.0); - // return Container( - // height: 50, - // width: MediaQuery.of(context).size.width * 0.5, - // color: Color.lerp(Colors.teal[100], Colors.teal[600], stuckValue), - // padding: const EdgeInsets.symmetric(horizontal: 16), - // alignment: Alignment.centerLeft, - // child: Row( - // children: [ - // Expanded( - // child: Text( - // 'Image #$index', - // style: const TextStyle(color: Colors.white), - // ), - // ), - // Offstage( - // offstage: stuckValue <= 0.0, - // child: Opacity( - // opacity: stuckValue, - // child: IconButton( - // icon: const Icon(Icons.image, color: Colors.white), - // onPressed: () => - // Scaffold.of(context).showSnackBar(SnackBar(content: Text('Favorite #$index'))), - // ), - // ), - // ), - // ], - // ), - // ); - // }, - // content: Container( - // // margin: EdgeInsets.all(8), - // height: 200, - // width: MediaQuery.of(context).size.width * 0.5, - // color: Colors.teal, - // child: Image.network(imageList[index], - // fit: BoxFit.cover, width: 100, height: 200), - // ), - // )) - // ); - - // Center( - // // alignment: AlignmentDirectional.center, - // child: ListView.builder( - // itemCount: imageList.length, - // itemBuilder: (context, index) => GFStickyHeaderBuilder( - // direction: Axis.vertical, - // // enableHeaderOverlap: true, - // // stickyContentPosition: GFPosition.end, - // stickyContentBuilder: - // (BuildContext context, double stuckValue) { - // stuckValue = 1.0 - stuckValue.clamp(0.0, 1.0); - // return Center( - // child: Container( - // height: 50.0, - // width: 100, - // color: - // Colors.grey[900].withOpacity(0.6 + stuckValue * 0.4), - // padding: EdgeInsets.symmetric(horizontal: 16.0), - // alignment: Alignment.centerLeft, - // child: Text( - // 'Header #$index', - // style: const TextStyle(color: Colors.white), - // ), - // ), - // ); - // }, - // content: Container( - // // margin: EdgeInsets.all(8), - // height: 200, - // width: 200, - // color: Colors.teal, - // child: Image.network(imageList[index], - // fit: BoxFit.cover, width: 100, height: 200.0), - // ), - // ))), - // ); - - Container( - height: 600, - color: Colors.teal, - child: ListView.builder( - itemCount: imageList.length, - itemBuilder: (context, index) => GFStickyHeader( - // enableHeaderOverlap: true, - direction: Axis.horizontal, - stickyContentPosition: GFPosition.end, - stickyContent: Container( - // alignment: AlignmentDirectional.centerEnd, - child: Container( - alignment: AlignmentDirectional.center, - height: 50, - width: 100, - color: Colors.blueGrey[700], - padding: EdgeInsets.symmetric(horizontal: 16.0), - child: Text( - 'Image $index', - style: const TextStyle(color: Colors.white), - ), - ), - ), - content: Container( - // height: 100, - width: 200, - child: Image.network( - imageList[index], - fit: BoxFit.cover, - height: 100, - ), - ), - )), - )); - -// ListView( -// children: [ -// Image.network(imageList[3], height: 200, fit: BoxFit.cover,), -// Image.network(imageList[4], height: 200, fit: BoxFit.cover,), -// GFStickyHeader( -// enableHeaderOverlap: true, -// stickyContent: Container( -// height: 100, -// color: Colors.teal, -// padding: const EdgeInsets.symmetric(horizontal: 16), -// alignment: Alignment.centerLeft, -// child: const Text( -// 'Title', -// style: TextStyle(color: Colors.white), -// ), -// ), -// content: ListView( -// shrinkWrap: true, -// physics: const ScrollPhysics(), -// children: [ -// Image.network(imageList[0], height: 200, fit: BoxFit.fill,), -// Image.network(imageList[1], height: 200, fit: BoxFit.fill,), -// Image.network(imageList[2], height: 200, fit: BoxFit.fill,), -// Image.network(imageList[0], height: 200, fit: BoxFit.fill,), -// Image.network(imageList[1], height: 200, fit: BoxFit.fill,), -// Image.network(imageList[2], height: 200, fit: BoxFit.fill,) -// ], -// ), -// ) -// ], -// ), -// ); - -// SingleChildScrollView( -// child: Column( -// mainAxisAlignment: MainAxisAlignment.start, -// crossAxisAlignment: CrossAxisAlignment.center, -// children: [ -// Container( -// height: 10, -// ), -// Radio( -// value: 0, -// groupValue: groupValue, -// onChanged: (val) { -// setState(() { -// groupValue = val; -// }); -// }, -// ), -// Radio( -// value: 1, -// groupValue: groupValue, -// onChanged: (val) { -// setState(() { -// groupValue = val; -// }); -// }, -// ), -// ], -// ), -// ), -// bottomSheet: GFBottomSheet( -// controller: _controller, -////animationDuration: 800, -//// minContentHeight: 100, -// maxContentHeight: 300, -//// elevation: 10, -// enableExpandableContent: true, -// stickyHeaderHeight: 100, -// stickyHeader: Container( -// decoration: BoxDecoration( -// borderRadius: BorderRadius.circular(10), -// color: Colors.tealAccent), -//// height: 100, -// child: const Center( -// child: Text('Swipe me!'), -// ), -// ), -// contentBody: Container( -// color: Colors.teal, -// child: ListView( -// shrinkWrap: true, -// physics: const ScrollPhysics(), -// children: const [ -// Text('fhj'), -// Text('fhj'), -// Text('fhj'), -// Text('fhj'), -// Text('fhj'), -// Text('fhj'), -// Text('fhj'), -// Text('fhj'), -// Text('fhj'), -// Text('fhj'), -// Text('fhj'), -// Text('fhj'), -// ], -// ), -// ), -//// stickyFooter: Container( -//// color: Theme.of(context).primaryColor, -//// height: 100, -//// child: const Center( -//// child: Text('I am Footer!'), -//// ), -//// ), -//// stickyFooterHeight: 50, -// ), -// floatingActionButton: FloatingActionButton( -// child: const Icon(Icons.stars), -// onPressed: () { -// _controller.isBottomSheetOpened -// ? _controller.hideBottomSheet() -// : _controller.showBottomSheet(); -// }), -// ); -} - -// void main() => runApp(MyApp()); -// -// class MyApp extends StatelessWidget { -// @override -// Widget build(BuildContext context) => MaterialApp( -// title: 'Example', -// theme: ThemeData( -// primarySwatch: Colors.blue, -// ), -// debugShowCheckedModeBanner: false, -// home: const MyHomePage(title: 'Example'), -// ); -// } -// -// class MyHomePage extends StatefulWidget { -// const MyHomePage({Key key, this.title}) : super(key: key); -// -// final String title; -// -// @override -// _MyHomePageState createState() => _MyHomePageState(); -// } -// -// class _MyHomePageState extends State -// with SingleTickerProviderStateMixin { -// -// List countryCode = [ -// {'name': 'India', 'dialCode': '91'}, -// {'name': 'xyz', 'dialCode': '92'}, -// {'name': 'abc', 'dialCode': '93'}, -// ]; -// -// @override -// Widget build(BuildContext context) => Scaffold( -// appBar: GFAppBar( -// title: const Text('UI KIt'), -// ), -// body: Scaffold( -// body: SingleChildScrollView( -// child: Column( -// mainAxisSize: MainAxisSize.min, -// mainAxisAlignment: MainAxisAlignment.start, -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// Container( -// margin: EdgeInsets.only(bottom: 35), -// child: Column( -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// Text( -// ' Mobile Number*', -// ), -// SizedBox( -// height: 6, -// ), -// Container( -// decoration: BoxDecoration( -// border: Border( -// bottom: BorderSide(color: Colors.white54, width: 1), -// )), -// child: Row( -// mainAxisSize: MainAxisSize.min, -// mainAxisAlignment: MainAxisAlignment.start, -// children: [ -// InkWell( -// onTap: () async { -// }, -// child: Container( -// height: 48, -// decoration: BoxDecoration( -// color: Colors.grey.withOpacity(0.5), -// // borderRadius: BorderRadius.all(Radius.circular(5.0)), -// // border: Border.all(color: Colors.green) -// ), -// child: Row( -// children: [ -// Text('+91' -// ), -// SizedBox( -// width: 6, -// ), -// Icon( -// Icons.keyboard_arrow_down, -// color: Colors.black, -// size: 16, -// ), -// SizedBox( -// width: 16, -// ), -// ], -// ), -// ), -// ), -// Expanded( -// child: TextFormField( -// keyboardType: TextInputType.number, -// validator: (String value) { -// if (value.isEmpty) { -// return 'Please Enter Mobile Number'; -// } else -// return null; -// }, -// decoration: new InputDecoration( -// hintText: 'Mobile Number', -// border: InputBorder.none, -// filled: true, -// fillColor: Colors.grey.withOpacity(0.2), -// focusedBorder: OutlineInputBorder( -// borderRadius: -// BorderRadius.all(Radius.circular(5.0)), -// borderSide: BorderSide(color: Colors.green)), -// ), -// ), -// ), -// ], -// ), -// ) -// ], -// )), -// SizedBox( -// height: 60, -// ), -// Container( -// margin: EdgeInsets.symmetric(horizontal: 16), -// child: TextFormField( -// // controller: _controller, -// decoration: InputDecoration( -// labelText: 'Search country', -// fillColor: Colors.white, -// focusedBorder: OutlineInputBorder( -// borderRadius: BorderRadius.circular(50.0), -// borderSide: BorderSide( -// color: Colors.grey, -// ), -// ), -// contentPadding: EdgeInsets.all(16), -// enabledBorder: OutlineInputBorder( -// borderRadius: BorderRadius.circular(50.0), -// borderSide: BorderSide( -// color: Colors.grey.withOpacity(0.6), -// width: 1.0, -// ), -// ), -// ), -// ), -// ), -// Container( -// height: MediaQuery.of(context).size.height - 120, -// child: ListView.builder( -// physics: ScrollPhysics(), -// shrinkWrap: true, -// itemCount: countryCode == null ? 0 : countryCode.length, -// itemBuilder: (BuildContext context, int index) { -// return Column( -// children: [ -// InkWell( -// onTap: () { -// Navigator.of(context).pop(countryCode[index]); -// }, -// child: ListTile( -// title: Text( -// countryCode[index]['name'], -// ), -// trailing: Text( -// '+${countryCode[index]['dialCode']}', -// ), -// ), -// ), -// Divider( -// height: 18, -// color: Colors.grey.withOpacity(0.5), -// ) -// ], -// ); -// })) -// ], -// ), -// ))); diff --git a/lib/components/sticky_header/render_gf_sticky_header.dart b/lib/components/sticky_header/render_gf_sticky_header.dart index 43b36ea0..24d3d0d9 100644 --- a/lib/components/sticky_header/render_gf_sticky_header.dart +++ b/lib/components/sticky_header/render_gf_sticky_header.dart @@ -151,7 +151,7 @@ class RenderGFStickyHeader extends RenderBox assert(constraints != null); double crossSize = 0; - double allotedSize = 0; + double allottedSize = 0; RenderBox child = firstChild; // ignore: unused_local_variable int totalChildren = 0; @@ -172,14 +172,14 @@ class RenderGFStickyHeader extends RenderBox break; } child.layout(innerConstraints, parentUsesSize: true); - allotedSize += _getMainSize(child); + allottedSize += _getMainSize(child); crossSize = math.max(crossSize, _getCrossSize(child)); } assert(child.parentData == childParentData); child = childParentData.nextSibling; } - final double idealSize = allotedSize; + final double idealSize = allottedSize; double actualSize; switch (_direction) { case Axis.horizontal: @@ -193,11 +193,11 @@ class RenderGFStickyHeader extends RenderBox crossSize = size.width; break; } - const double leadingSpace = 0; + const double startingSpace = 0; const double betweenSpace = 0; const bool flipMainAxis = !true; double childMainPosition = - flipMainAxis ? actualSize - leadingSpace : leadingSpace; + flipMainAxis ? actualSize - startingSpace : startingSpace; child = _contentBody; // ignore: invariant_booleans while (child != null) { From e99fedbafcb91a863602648e2213a9194175d910 Mon Sep 17 00:00:00 2001 From: deepikahr Date: Thu, 24 Sep 2020 19:28:37 +0530 Subject: [PATCH 13/13] GFStickyHeader component completed and tested --- lib/components/sticky_header/render_gf_sticky_header.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/components/sticky_header/render_gf_sticky_header.dart b/lib/components/sticky_header/render_gf_sticky_header.dart index 24d3d0d9..1d550be0 100644 --- a/lib/components/sticky_header/render_gf_sticky_header.dart +++ b/lib/components/sticky_header/render_gf_sticky_header.dart @@ -35,6 +35,7 @@ class RenderGFStickyHeader extends RenderBox final bool _enableHeaderOverlap; final GFPosition _stickyContentPosition; + Axis get direction => _direction; Axis _direction; set direction(Axis value) {