I've created a flutter widget that consists of a few slivers that make up the app bar and tabs, and below that, I have the TabBarView.
1) There is an excessive amount of padding between the tabs and the body ScrollView, but I'm not sure what element is causing it.
2) When scrolling the content in the ListView, it shows behind the tab and I'm not sure what I need to do to prevent that.
3)Lastly, You can scroll until there are no more visible items in the ListView, how would you scroll similar to html/css overflow, where it stops scrolling at the end of the content?
Here's my view
import 'package:flutter/material.dart';
import 'package:nssa/bloc/conference/bloc.dart';
class ConferencePage extends StatelessWidget {
final Conference conference;
ConferencePage(@required this.conference);
TabBar getTabBar(List<Zone> zones) {
return TabBar(
tabs: zones.map((zone) {
return new Tab(text: zone.type);
}).toList(growable: false)
);
}
TabBarView getTabBody(List<Zone> zones) {
return TabBarView(
children: zones.map((zone) {
return ListView(
children: zone.upcomingEvents().map((event) {
return new ListTile(title: Text(event.name));
}).toList(growable: false)
);
}).toList(growable: false)
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: DefaultTabController(
length: this.conference.zones.length,
child: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
expandedHeight: 200.0,
floating: true,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text(
this.conference.name,
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
)
),
background: Hero(
tag: this.conference.name,
child: Image.network(
this.conference.image,
fit: BoxFit.cover,
)
)
),
),
SliverPersistentHeader(
delegate: _SliverAppBarDelegate(
this.getTabBar(this.conference.zones),
),
pinned: true,
),
];
},
body: this.getTabBody(this.conference.zones)
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.card_membership)
),
);
}
}
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
_SliverAppBarDelegate(this._tabBar);
final TabBar _tabBar;
@override
double get minExtent => _tabBar.preferredSize.height;
@override
double get maxExtent => _tabBar.preferredSize.height;
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return new Container(
child: _tabBar,
);
}
@override
bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
return false;
}
}
Here are my models
import 'package:flutter/foundation.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
import 'package:intl/intl.dart';
import 'dart:developer';
@immutable
class Zone extends Equatable {
final String type;
final List<String> divisions;
final List<Event> events;
final DateTime _now = DateTime.now();
Zone (
@required this.type,
@required this.divisions,
@required this.events,
);
@override
List<Object> get props => [type, /*divisions,*/ events];
Event currentEvent() {
return this.events.firstWhere((event) => event.startDate.isBefore(_now) && event.endDate.isAfter(_now), orElse: () => null);
}
List<Event> pastEvents() {
return this.events.where((event) => event.endDate.isBefore(_now)).toList(growable: false);
}
List<Event> upcomingEvents() {
return this.events.where((event) => event.startDate.isAfter(_now)).toList(growable: false);
}
factory Zone.fromJson(Map<String, dynamic> zoneJSON) {
List<String> divisions = (zoneJSON['divisions'] as List).map((division) {
return division.toString();
}).toList(growable: true);
List<Event> events = (zoneJSON['events'] as List).map((event) {
return new Event.fromJson(event);
}).toList(growable: true);
events.sort((a,b) => a.startDate.compareTo(b.startDate));
return Zone(
zoneJSON['type'],
divisions,
events,
);
}
}
@immutable
class Event extends Equatable {
final String name;
final String details;
final DateTime startDate;
final DateTime endDate;
final String image;
final String ticketURL;
Event (
@required this.name,
@required this.details,
@required this.startDate,
@required this.endDate,
@required this.image,
@required this.ticketURL,
);
@override
List<Object> get props => [name, startDate, endDate, details, image, ticketURL];
factory Event.fromJson(Map<String, dynamic> eventJSON) {
DateFormat dateFormat = DateFormat("yyyy-MM-dd");
return Event(
eventJSON['name'],
eventJSON['details'],
dateFormat.parse(eventJSON['startDate'].toString()),
dateFormat.parse(eventJSON['endDate'].toString()),
eventJSON['image'],
eventJSON['ticketURL']
);
}
}
The problem can be solved by moving the SliverAppBar into the CustomScrollView and not use the NestedScrollView at all.
NestedScrollView is just like ScrollView , but it supports acting as both a nested scrolling parent and child on both new and old versions of Android. Nested scrolling is enabled by default.
A scrolling view inside of which can be nested other scrolling views, with their scroll positions being intrinsically linked.
It is simple to add scrollable on flutter. Just create SingleChildScrollView widget as a TabBarView and put all the contents as it's child.
When using a NestedScrollView, you might need to wrap your SliverAppBar with a SliverOverlapAbsorber to avoid having the body content scrolling behing the appbar.
Also, you might be better off putting your TabBar in the bottom
of your SliverAppBar, just like in the official example.
See documentation here: https://api.flutter.dev/flutter/widgets/NestedScrollView-class.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With