How would I set the height of a Widget in a ListView depending on the offset of the underlying scrollview?
Thanks for any hints :)
To know the current scroll offset of ListView, you can just pass a controller and check the offset of controller.
example:
class ScrollOffsetWidget extends StatefulWidget {
#override
_ScrollOffsetWidgetState createState() => new _ScrollOffsetWidgetState();
}
class _ScrollOffsetWidgetState extends State<ScrollOffsetWidget> {
List<double> height = <double>[200.0,200.0,200.0,200.0,200.0];
double scrollOffset = 0.0;
static ScrollController _controller;
_ScrollOffsetWidgetState(){
_controller = new ScrollController(initialScrollOffset: scrollOffset);
_controller.addListener(listen);
}
void listen(){
final List<double> newHeight = height;
if(_controller.offset>100.0){
newHeight[1] = 400.0;
}else{
newHeight[1] = 200.0;
}
setState((){
height = newHeight;
});
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
body: new ListView(
controller: _controller,
children: <Widget>[
new AnimatedContainer(duration: const Duration(milliseconds: 300),height: height[0],color: Colors.primaries[0],),
new AnimatedContainer(duration: const Duration(milliseconds: 300),height: height[1],color: Colors.primaries[1],),
new AnimatedContainer(duration: const Duration(milliseconds: 300),height: height[2],color: Colors.primaries[2],),
new AnimatedContainer(duration: const Duration(milliseconds: 300),height: height[3],color: Colors.primaries[3],),
new AnimatedContainer(duration: const Duration(milliseconds: 300),height: height[4],color: Colors.primaries[4],)
],
),
),
);
}
}
Hope that helped!
Related
I have List array which is based on flutter multi_image_picker: ^4.7.14 dependency. How can I convert List Asset Array to List File Array?
This is the code
class _ConvertImageState extends State<ConvertImage> {
List<Asset> images = List<Asset>();
#override
void initState() {
super.initState();
}
Future<void> pickImages() async {
List<Asset> resultList = List<Asset>();
try {
resultList = await MultiImagePicker.pickImages(
maxImages: 20,
enableCamera: true,
selectedAssets: images,
materialOptions: MaterialOptions(
selectCircleStrokeColor: "white",
actionBarTitle: "Select Images",
allViewTitle: "All Images",
actionBarColor: "#3b3b3b",
actionBarTitleColor: "white",
statusBarColor: '#bbbbbb',
selectionLimitReachedText: "You can select minimum 3 and maximum 20 images",
),
);
} on Exception catch (e) {
print(e);
}
setState(() {
images = resultList;
});
}
#override
Widget build(BuildContext context) {......}
}
Can I do it with a loop? Please, anyone can tell me the correct way?
Here I find an answer. This is my working code. I think it is correct.
class _ConvertImageState extends State<ConvertImage> {
List<Asset> images = List<Asset>();
List<File> fileImageArray = [];
#override
void initState() {
super.initState();
}
Future<void> pickImages() async {
List<Asset> resultList = List<Asset>();
try {
resultList = await MultiImagePicker.pickImages(
maxImages: 20,
enableCamera: true,
selectedAssets: images,
materialOptions: MaterialOptions(
selectCircleStrokeColor: "white",
actionBarTitle: "Select Images",
allViewTitle: "All Images",
actionBarColor: "#3b3b3b",
actionBarTitleColor: "white",
statusBarColor: '#bbbbbb',
selectionLimitReachedText: "You can select minimum 3 and maximum 20 images",
),
);
} on Exception catch (e) {
print(e);
}
setState(() {
images = resultList;
putToFileArray();
});
}
void putToFileArray(){
fileImagesArray.clear();
images.forEach((imageAsset) async {
final filePath =
await FlutterAbsolutePath.getAbsolutePath(imageAsset.identifier);
File tempFile = File(filePath);
if (tempFile.existsSync()) {
fileImageArray.add(tempFile);
}
});
}
#override
Widget build(BuildContext context) {......}
}
FlutterAbsolutePathClass (Dependency)
flutter_absolute_path: ^1.0.6
Link: https://pub.dev/packages/flutter_absolute_path
To Print Array
onPressed: () {
print(fileImageArray.toString());
};
I want to connect to my firebase database as now called "realtime database".
My Apps is running smoothly, but I am getting following error if I want to start the method in my app through a button:
======== Exception caught by widgets library =======================================================
The following NoSuchMethodError was thrown building LoadDataFromFireStore(dirty, state: LoadDataFromFireStoreState#20451):
The getter 'keys' was called on null.
Receiver: null
Tried calling: keys
The relevant error-causing widget was:
LoadDataFromFireStore file:///C:/Users/Nutzer/AndroidStudioProjects/tennis_sv_schwaig/lib/widget/kalender.dart:17:13
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
#1 LoadDataFromFireStoreState._showCalendar (package:tennis_sv_schwaig/widget/kalender.dart:58:34)
#2 LoadDataFromFireStoreState.build (package:tennis_sv_schwaig/widget/kalender.dart:49:13)
#3 StatefulElement.build (package:flutter/src/widgets/framework.dart:4744:28)
#4 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4627:15)
...
====================================================================================================
I do not know if theres is a collision with a keyForm in an other method before. Could this be possible?
Here is the code from the method:
import 'dart:math';
import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
import 'package:intl/intl.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() => runApp(kalender());
class kalender extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: LoadDataFromFireStore(),
);
}
}
class LoadDataFromFireStore extends StatefulWidget {
#override
LoadDataFromFireStoreState createState() => LoadDataFromFireStoreState();
}
class LoadDataFromFireStoreState extends State<LoadDataFromFireStore> {
DataSnapshot querySnapshot;
dynamic data;
List<Color> _colorCollection;
#override
void initState() {
_initializeEventColor();
getDataFromDatabase().then((results) {
setState(() {
if (results != null) {
querySnapshot = results;
}
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _showCalendar(),
);
}
_showCalendar() {
if (querySnapshot != null) {
List<Meeting> collection;
var showData = querySnapshot.value;
Map<dynamic, dynamic> values = showData;
List<dynamic> key = values.keys.toList();
if (values != null) {
for (int i = 0; i < key.length; i++) {
data = values[key[i]];
collection ??= <Meeting>[];
final Random random = new Random();
collection.add(Meeting(
eventName: data['Subject'],
isAllDay: false,
from: DateFormat('dd/MM/yyyy HH:mm:ss').parse(data['StartTime']),
to: DateFormat('dd/MM/yyyy HH:mm:ss').parse(data['EndTime']),
background: _colorCollection[random.nextInt(9)]));
}
} else {
return Center(
child: CircularProgressIndicator(),
);
}
return SafeArea(
child: Column(
children: [
Container(
height: 400,
child: SfCalendar(
view: CalendarView.month,
initialDisplayDate: DateTime(2020, 4, 5, 9, 0, 0),
dataSource: _getCalendarDataSource(collection),
monthViewSettings: MonthViewSettings(showAgenda: true),
),
),
RaisedButton(onPressed: () {
final dbRef = FirebaseDatabase.instance.reference().child("CalendarData");
dbRef.push().set({
"StartTime": '07/04/2020 07:00:00',
"EndTime": '07/04/2020 08:00:00',
"Subject":'NewMeeting',
}).then((_) {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('Successfully Added')));
}).catchError((onError) {
print(onError);
});
}, child: Text("Add")),
RaisedButton(onPressed: () {
final dbRef = FirebaseDatabase.instance.reference().child("CalendarData");
dbRef.remove();
}, child: Text("Delete")),
],
));
}
}
void _initializeEventColor() {
this._colorCollection = new List<Color>();
_colorCollection.add(const Color(0xFF0F8644));
_colorCollection.add(const Color(0xFF8B1FA9));
_colorCollection.add(const Color(0xFFD20100));
_colorCollection.add(const Color(0xFFFC571D));
_colorCollection.add(const Color(0xFF36B37B));
_colorCollection.add(const Color(0xFF01A1EF));
_colorCollection.add(const Color(0xFF3D4FB5));
_colorCollection.add(const Color(0xFFE47C73));
_colorCollection.add(const Color(0xFF636363));
_colorCollection.add(const Color(0xFF0A8043));
}
}
MeetingDataSource _getCalendarDataSource([List<Meeting> collection]) {
List<Meeting> meetings = collection ?? <Meeting>[];
return MeetingDataSource(meetings);
}
class MeetingDataSource extends CalendarDataSource {
MeetingDataSource(List<Meeting> source) {
appointments = source;
}
#override
DateTime getStartTime(int index) {
return appointments[index].from;
}
#override
DateTime getEndTime(int index) {
return appointments[index].to;
}
#override
bool isAllDay(int index) {
return appointments[index].isAllDay;
}
#override
String getSubject(int index) {
return appointments[index].eventName;
}
#override
Color getColor(int index) {
return appointments[index].background;
}
}
getDataFromDatabase() async {
var value = FirebaseDatabase.instance.reference();
var getValue = await value.child('CalendarData').once();
return getValue;
}
class Meeting {
Meeting({this.eventName, this.from, this.to, this.background, this.isAllDay});
String eventName;
DateTime from;
DateTime to;
Color background;
bool isAllDay;
}
Hope its enough and anybody could help. Thanks a lot!
I tried to display a timer ( format dd HH mm ss ) to count the time between each actions (button action for exemple ). And need to work even the app is close and rebuild. Currently I load a string date I saved with sharedpreference when I pressed a button who represent the time when I pressed the button. I format all time decimal to compare and display time difference. I think it's not beautifull, not what I search, and I don't succeded to display clock in the format (dd HH mm ss). If someone have a more simple exemple :)
load_records_pulsion() async{
/*var current_time = DateFormat('yyyy-MM-dd HH').format(DateTime.now());*/
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
RegExp regExp = new RegExp( //Here is the regex time pulsion
r"([12]\d{3})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])",
);
last_pulsion = (prefs.getString('last_pulsion'))??0;
var match = regExp.firstMatch("$last_pulsion");
annees = match.group(1); // hh:mm
mois = match.group(2); // hh:mm
jours = match.group(3); // hh:mm
int annees_int = int.tryParse("$annees") ;
int mois_int = int.tryParse("$mois") ;
int jours_int = int.tryParse("$jours") ;
print("$annees_int");
print("$mois_int");
print("$jours_int");
final last_pulsion2 = DateTime(annees_int, mois_int, jours_int);
final date_now = DateTime.now();
difference_pulsion = date_now.difference(last_pulsion2).inDays;
if(difference_pulsion==0){
difference_pulsion ="";
prefix_pulsion ="Aujourd'hui";
}else{
prefix_pulsion ="jours";
}
});
}
Also I tried this code, it's OK the timer is increase when I call the function, but I don't want datenow, I just need to start with zero time
int _start = 0;
void startTimer() {
_start=0;
var now = new DateTime.now();
const oneSec = const Duration(seconds: 1);
_timer = new Timer.periodic(
oneSec,
(Timer timer) => setState(() {
{
chrono = now.add(new Duration(seconds: _start));
_start = _start + 1;
}
}));
}
Edit: I found this solution but have some lifecycle error, and if I close the app, I loose the timer.
Stopwatch stopwatch = new Stopwatch();
void rightButtonPressed() {
setState(() {
if (stopwatch.isRunning) {
stopwatch.reset();
} else {
stopwatch.reset();
stopwatch.start();
}
});
}
#override
Widget build(BuildContext context)
{
...
new Container(height: 80.0,
child: new Center(
child: new TimerText(stopwatch: stopwatch),
)),
...
class TimerText extends StatefulWidget {
TimerText({this.stopwatch});
final Stopwatch stopwatch;
TimerTextState createState() => new TimerTextState(stopwatch: stopwatch);
}
class TimerTextState extends State<TimerText> {
Timer timer;
final Stopwatch stopwatch;
TimerTextState({this.stopwatch}) {
timer = new Timer.periodic(new Duration(milliseconds: 30), callback);
}
void callback(Timer timer) {
if (stopwatch.isRunning) {
setState(() {
});
}
}
#override
Widget build(BuildContext context) {
final TextStyle timerTextStyle = const TextStyle(fontSize: 50.0, fontFamily: "Open Sans");
String formattedTime = TimerTextFormatter.format(stopwatch.elapsedMilliseconds);
return new Text(formattedTime, style: timerTextStyle);
}
}
class TimerTextFormatter {
static String format(int milliseconds) {
int seconds = (milliseconds / 1000).truncate();
int minutes = (seconds / 60).truncate();
int hours = (minutes / 60).truncate();
int days = (hours / 24).truncate();
String minutesStr = (minutes % 60).toString().padLeft(2, '0');
String secondsStr = (seconds % 60).toString().padLeft(2, '0');
String hoursStr = (hours % 60).toString().padLeft(2, '0');
String daysStr = (days % 24).toString().padLeft(2, '0');
return "$daysStr:$hoursStr:$minutesStr:$secondsStr";
}
}
If you want the counter to persist after closing the app, there is no way around saving the value somewhere (like shared preferences).
Using dateTime.toIso8601String() and DateTime.parse() will make the saving and loading less ugly.
To calculate the passed time you can use DateTime.now().difference(lastButtonPressed)
There should be a function to format Duration (https://api.flutter.dev/flutter/intl/DateFormat/formatDurationFrom.html) but it's not implemented yet. I found one here: Formatting a Duration like HH:mm:ss
Here is a little example:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutterfly/SharedPrefs.dart';
class TestWidget extends StatefulWidget {
#override
_TestWidgetState createState() => _TestWidgetState();
}
class _TestWidgetState extends State<TestWidget> {
DateTime _lastButtonPress;
String _pressDuration;
Timer _ticker;
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Time since button pressed"),
Text(_pressDuration),
RaisedButton(
child: Text("Press me"),
onPressed: () {
_lastButtonPress = DateTime.now();
_updateTimer();
sharedPreferences.setString("lastButtonPress",_lastButtonPress.toIso8601String());
},
)
],
),
);
}
#override
void initState() {
super.initState();
final lastPressString = sharedPreferences.getString("lastButtonPress");
_lastButtonPress = lastPressString!=null ? DateTime.parse(lastPressString) : DateTime.now();
_updateTimer();
_ticker = Timer.periodic(Duration(seconds:1),(_)=>_updateTimer());
}
#override
void dispose() {
_ticker.cancel();
super.dispose();
}
void _updateTimer() {
final duration = DateTime.now().difference(_lastButtonPress);
final newDuration = _formatDuration(duration);
setState(() {
_pressDuration = newDuration;
});
}
String _formatDuration(Duration duration) {
String twoDigits(int n) {
if (n >= 10) return "$n";
return "0$n";
}
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return "${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds";
}
}
For simplicity i initialized shared preferences in the main method in global scope.
I would like to show a calendar not as a dialog. And I'll like the use to be able to select date interval as in the screenshot.
I would recommend you not to reinvent the wheel and pick one of the community calendar widgets (like that one), but in case you need a custom solution, you may start with something really simple. For example, if you need to pick a range you may just take a grid and a few buttons like that:
import 'package:flutter/material.dart';
class CalendarPage extends StatefulWidget {
final String title;
CalendarPage({Key key, this.title}) : super(key: key);
#override
State<StatefulWidget> createState() => _CalendarPageState();
}
class _CalendarPageState extends State<CalendarPage> {
int _left = -1;
int _right = -1;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: GridView.count(
crossAxisCount: 7,
children: List.generate(31, (index) {
return Container(
decoration: BoxDecoration(
border: Border.all(width: 2.0, color: Colors.black38),
color: _isInBounds(index)
? Colors.yellow[100]
: Colors.transparent,
borderRadius: const BorderRadius.all(const Radius.circular(8.0)),
),
margin: const EdgeInsets.all(2.0),
child: FlatButton(
onPressed: () => _handleTap(index),
child: Text('${index + 1}',
style: Theme.of(context).textTheme.body2,
textAlign: TextAlign.center)));
}),
));
}
void _handleTap(index) {
setState(() {
if (_left == -1)
_left = index;
else if (_right == -1) _right = index;
});
}
bool _isInBounds(int index) => _left <= index && index <= _right;
}
UI: https://flutter.io/tutorials/layout/
Selecting a range: https://www.didierboelens.com/2018/07/range-slider/
You'll learn a lot from these. Good luck!
I am trying to program a weekview like in the google calendar for flutter.
I want to display a timetable where the events begin and end at individual times (e.g. 9:34, 17:11) and at exactly the times they should be displayed in the calendar. So not rounded up or down to the full hour as I currently implemented it with a table widget.
What would be the best way to achieve such an view. I don't know where to start. The horizontal scrolling itself isn't so important. But the vertical one is my problem. How do I place these event widgets at a specific position in a scrollable list?
I appreciate every kind of answer. It would help me very much.
Here is a poor mans version, using a CustomScrollView and simple Stacks with Positioned children.
Things are getting more difficult if you also want horizontal scrolling. For really complex layouts with animations, you may need a custom layout.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Playground',
home: TestPage(),
);
}
}
class TestPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Calendar'),
),
body: WeekView(),
);
}
}
const headerHeight = 50.0;
const hourHeight = 100.0;
class WeekView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: <Widget>[
SliverPersistentHeader(
delegate: WeekViewHeaderDelegate(),
pinned: true,
),
SliverToBoxAdapter(
child: _buildGrid(),
)
],
);
}
Widget _buildGrid() {
return SizedBox(
height: hourHeight * 24,
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: List.generate(7, (d) => _buildColumn(d)),
),
);
}
Widget _buildColumn(int d) {
return Expanded(
child: Stack(
children: <Widget>[
Positioned(
left: 0.0,
top: d * 25.0,
right: 0.0,
height: 50.0 * (d + 1),
child: Container(
margin: EdgeInsets.symmetric(horizontal: 2.0),
color: Colors.orange[100 + d * 100],
),
)
],
),
);
}
}
class WeekViewHeaderDelegate extends SliverPersistentHeaderDelegate {
#override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
color: Colors.red.withOpacity(0.5),
child: Center(
child: Text('HEADER'),
),
);
}
#override
double get maxExtent => headerHeight;
#override
double get minExtent => headerHeight;
#override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
return false;
}
}
I'm a bit late here but I've created a library that can offer you exactly what you want :
It's called FlutterWeekView and here are some links if you're still interested :
pub.dev
Github
Preview :