👽 Make poll load itself to match server updates
This commit is contained in:
@@ -8,7 +8,7 @@ part 'poll.g.dart';
|
||||
@freezed
|
||||
sealed class SnPollWithStats with _$SnPollWithStats {
|
||||
const factory SnPollWithStats({
|
||||
required Map<String, dynamic>? userAnswer,
|
||||
required SnPollAnswer? userAnswer,
|
||||
@Default({}) Map<String, dynamic> stats,
|
||||
required String id,
|
||||
required List<SnPollQuestion> questions,
|
||||
|
||||
@@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$SnPollWithStats {
|
||||
|
||||
Map<String, dynamic>? get userAnswer; Map<String, dynamic> get stats; String get id; List<SnPollQuestion> get questions; String? get title; String? get description; DateTime? get endedAt; String get publisherId; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
||||
SnPollAnswer? get userAnswer; Map<String, dynamic> get stats; String get id; List<SnPollQuestion> get questions; String? get title; String? get description; DateTime? get endedAt; String get publisherId; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
||||
/// Create a copy of SnPollWithStats
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -28,12 +28,12 @@ $SnPollWithStatsCopyWith<SnPollWithStats> get copyWith => _$SnPollWithStatsCopyW
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPollWithStats&&const DeepCollectionEquality().equals(other.userAnswer, userAnswer)&&const DeepCollectionEquality().equals(other.stats, stats)&&(identical(other.id, id) || other.id == id)&&const DeepCollectionEquality().equals(other.questions, questions)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.endedAt, endedAt) || other.endedAt == endedAt)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPollWithStats&&(identical(other.userAnswer, userAnswer) || other.userAnswer == userAnswer)&&const DeepCollectionEquality().equals(other.stats, stats)&&(identical(other.id, id) || other.id == id)&&const DeepCollectionEquality().equals(other.questions, questions)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.endedAt, endedAt) || other.endedAt == endedAt)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(userAnswer),const DeepCollectionEquality().hash(stats),id,const DeepCollectionEquality().hash(questions),title,description,endedAt,publisherId,createdAt,updatedAt,deletedAt);
|
||||
int get hashCode => Object.hash(runtimeType,userAnswer,const DeepCollectionEquality().hash(stats),id,const DeepCollectionEquality().hash(questions),title,description,endedAt,publisherId,createdAt,updatedAt,deletedAt);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
@@ -48,11 +48,11 @@ abstract mixin class $SnPollWithStatsCopyWith<$Res> {
|
||||
factory $SnPollWithStatsCopyWith(SnPollWithStats value, $Res Function(SnPollWithStats) _then) = _$SnPollWithStatsCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
Map<String, dynamic>? userAnswer, Map<String, dynamic> stats, String id, List<SnPollQuestion> questions, String? title, String? description, DateTime? endedAt, String publisherId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||
SnPollAnswer? userAnswer, Map<String, dynamic> stats, String id, List<SnPollQuestion> questions, String? title, String? description, DateTime? endedAt, String publisherId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||
});
|
||||
|
||||
|
||||
|
||||
$SnPollAnswerCopyWith<$Res>? get userAnswer;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
@@ -68,7 +68,7 @@ class _$SnPollWithStatsCopyWithImpl<$Res>
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? userAnswer = freezed,Object? stats = null,Object? id = null,Object? questions = null,Object? title = freezed,Object? description = freezed,Object? endedAt = freezed,Object? publisherId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
userAnswer: freezed == userAnswer ? _self.userAnswer : userAnswer // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>?,stats: null == stats ? _self.stats : stats // ignore: cast_nullable_to_non_nullable
|
||||
as SnPollAnswer?,stats: null == stats ? _self.stats : stats // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,questions: null == questions ? _self.questions : questions // ignore: cast_nullable_to_non_nullable
|
||||
as List<SnPollQuestion>,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
|
||||
@@ -81,7 +81,19 @@ as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ign
|
||||
as DateTime?,
|
||||
));
|
||||
}
|
||||
/// Create a copy of SnPollWithStats
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnPollAnswerCopyWith<$Res>? get userAnswer {
|
||||
if (_self.userAnswer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnPollAnswerCopyWith<$Res>(_self.userAnswer!, (value) {
|
||||
return _then(_self.copyWith(userAnswer: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +172,7 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( Map<String, dynamic>? userAnswer, Map<String, dynamic> stats, String id, List<SnPollQuestion> questions, String? title, String? description, DateTime? endedAt, String publisherId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( SnPollAnswer? userAnswer, Map<String, dynamic> stats, String id, List<SnPollQuestion> questions, String? title, String? description, DateTime? endedAt, String publisherId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SnPollWithStats() when $default != null:
|
||||
return $default(_that.userAnswer,_that.stats,_that.id,_that.questions,_that.title,_that.description,_that.endedAt,_that.publisherId,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
||||
@@ -181,7 +193,7 @@ return $default(_that.userAnswer,_that.stats,_that.id,_that.questions,_that.titl
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( Map<String, dynamic>? userAnswer, Map<String, dynamic> stats, String id, List<SnPollQuestion> questions, String? title, String? description, DateTime? endedAt, String publisherId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( SnPollAnswer? userAnswer, Map<String, dynamic> stats, String id, List<SnPollQuestion> questions, String? title, String? description, DateTime? endedAt, String publisherId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SnPollWithStats():
|
||||
return $default(_that.userAnswer,_that.stats,_that.id,_that.questions,_that.title,_that.description,_that.endedAt,_that.publisherId,_that.createdAt,_that.updatedAt,_that.deletedAt);}
|
||||
@@ -198,7 +210,7 @@ return $default(_that.userAnswer,_that.stats,_that.id,_that.questions,_that.titl
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( Map<String, dynamic>? userAnswer, Map<String, dynamic> stats, String id, List<SnPollQuestion> questions, String? title, String? description, DateTime? endedAt, String publisherId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( SnPollAnswer? userAnswer, Map<String, dynamic> stats, String id, List<SnPollQuestion> questions, String? title, String? description, DateTime? endedAt, String publisherId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _SnPollWithStats() when $default != null:
|
||||
return $default(_that.userAnswer,_that.stats,_that.id,_that.questions,_that.title,_that.description,_that.endedAt,_that.publisherId,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
|
||||
@@ -213,18 +225,10 @@ return $default(_that.userAnswer,_that.stats,_that.id,_that.questions,_that.titl
|
||||
@JsonSerializable()
|
||||
|
||||
class _SnPollWithStats implements SnPollWithStats {
|
||||
const _SnPollWithStats({required final Map<String, dynamic>? userAnswer, final Map<String, dynamic> stats = const {}, required this.id, required final List<SnPollQuestion> questions, this.title, this.description, this.endedAt, required this.publisherId, required this.createdAt, required this.updatedAt, this.deletedAt}): _userAnswer = userAnswer,_stats = stats,_questions = questions;
|
||||
const _SnPollWithStats({required this.userAnswer, final Map<String, dynamic> stats = const {}, required this.id, required final List<SnPollQuestion> questions, this.title, this.description, this.endedAt, required this.publisherId, required this.createdAt, required this.updatedAt, this.deletedAt}): _stats = stats,_questions = questions;
|
||||
factory _SnPollWithStats.fromJson(Map<String, dynamic> json) => _$SnPollWithStatsFromJson(json);
|
||||
|
||||
final Map<String, dynamic>? _userAnswer;
|
||||
@override Map<String, dynamic>? get userAnswer {
|
||||
final value = _userAnswer;
|
||||
if (value == null) return null;
|
||||
if (_userAnswer is EqualUnmodifiableMapView) return _userAnswer;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(value);
|
||||
}
|
||||
|
||||
@override final SnPollAnswer? userAnswer;
|
||||
final Map<String, dynamic> _stats;
|
||||
@override@JsonKey() Map<String, dynamic> get stats {
|
||||
if (_stats is EqualUnmodifiableMapView) return _stats;
|
||||
@@ -261,12 +265,12 @@ Map<String, dynamic> toJson() {
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPollWithStats&&const DeepCollectionEquality().equals(other._userAnswer, _userAnswer)&&const DeepCollectionEquality().equals(other._stats, _stats)&&(identical(other.id, id) || other.id == id)&&const DeepCollectionEquality().equals(other._questions, _questions)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.endedAt, endedAt) || other.endedAt == endedAt)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPollWithStats&&(identical(other.userAnswer, userAnswer) || other.userAnswer == userAnswer)&&const DeepCollectionEquality().equals(other._stats, _stats)&&(identical(other.id, id) || other.id == id)&&const DeepCollectionEquality().equals(other._questions, _questions)&&(identical(other.title, title) || other.title == title)&&(identical(other.description, description) || other.description == description)&&(identical(other.endedAt, endedAt) || other.endedAt == endedAt)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_userAnswer),const DeepCollectionEquality().hash(_stats),id,const DeepCollectionEquality().hash(_questions),title,description,endedAt,publisherId,createdAt,updatedAt,deletedAt);
|
||||
int get hashCode => Object.hash(runtimeType,userAnswer,const DeepCollectionEquality().hash(_stats),id,const DeepCollectionEquality().hash(_questions),title,description,endedAt,publisherId,createdAt,updatedAt,deletedAt);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
@@ -281,11 +285,11 @@ abstract mixin class _$SnPollWithStatsCopyWith<$Res> implements $SnPollWithStats
|
||||
factory _$SnPollWithStatsCopyWith(_SnPollWithStats value, $Res Function(_SnPollWithStats) _then) = __$SnPollWithStatsCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
Map<String, dynamic>? userAnswer, Map<String, dynamic> stats, String id, List<SnPollQuestion> questions, String? title, String? description, DateTime? endedAt, String publisherId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||
SnPollAnswer? userAnswer, Map<String, dynamic> stats, String id, List<SnPollQuestion> questions, String? title, String? description, DateTime? endedAt, String publisherId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||
});
|
||||
|
||||
|
||||
|
||||
@override $SnPollAnswerCopyWith<$Res>? get userAnswer;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
@@ -300,8 +304,8 @@ class __$SnPollWithStatsCopyWithImpl<$Res>
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? userAnswer = freezed,Object? stats = null,Object? id = null,Object? questions = null,Object? title = freezed,Object? description = freezed,Object? endedAt = freezed,Object? publisherId = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||
return _then(_SnPollWithStats(
|
||||
userAnswer: freezed == userAnswer ? _self._userAnswer : userAnswer // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>?,stats: null == stats ? _self._stats : stats // ignore: cast_nullable_to_non_nullable
|
||||
userAnswer: freezed == userAnswer ? _self.userAnswer : userAnswer // ignore: cast_nullable_to_non_nullable
|
||||
as SnPollAnswer?,stats: null == stats ? _self._stats : stats // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,questions: null == questions ? _self._questions : questions // ignore: cast_nullable_to_non_nullable
|
||||
as List<SnPollQuestion>,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
|
||||
@@ -315,7 +319,19 @@ as DateTime?,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of SnPollWithStats
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnPollAnswerCopyWith<$Res>? get userAnswer {
|
||||
if (_self.userAnswer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnPollAnswerCopyWith<$Res>(_self.userAnswer!, (value) {
|
||||
return _then(_self.copyWith(userAnswer: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,12 @@ part of 'poll.dart';
|
||||
|
||||
_SnPollWithStats _$SnPollWithStatsFromJson(Map<String, dynamic> json) =>
|
||||
_SnPollWithStats(
|
||||
userAnswer: json['user_answer'] as Map<String, dynamic>?,
|
||||
userAnswer:
|
||||
json['user_answer'] == null
|
||||
? null
|
||||
: SnPollAnswer.fromJson(
|
||||
json['user_answer'] as Map<String, dynamic>,
|
||||
),
|
||||
stats: json['stats'] as Map<String, dynamic>? ?? const {},
|
||||
id: json['id'] as String,
|
||||
questions:
|
||||
@@ -32,7 +37,7 @@ _SnPollWithStats _$SnPollWithStatsFromJson(Map<String, dynamic> json) =>
|
||||
|
||||
Map<String, dynamic> _$SnPollWithStatsToJson(_SnPollWithStats instance) =>
|
||||
<String, dynamic>{
|
||||
'user_answer': instance.userAnswer,
|
||||
'user_answer': instance.userAnswer?.toJson(),
|
||||
'stats': instance.stats,
|
||||
'id': instance.id,
|
||||
'questions': instance.questions.map((e) => e.toJson()).toList(),
|
||||
|
||||
@@ -7,7 +7,7 @@ part of 'activity_rpc.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$presenceActivitiesHash() =>
|
||||
r'dcea3cad01b4010c0087f5281413d83a754c2a17';
|
||||
r'3bfaa638eeb961ecd62a32d6a7760a6a7e7bf6f2';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
||||
@@ -8,11 +8,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/screens/about.dart';
|
||||
import 'package:island/screens/developers/app_detail.dart';
|
||||
import 'package:island/screens/developers/bot_detail.dart';
|
||||
import 'package:island/screens/developers/edit_app.dart';
|
||||
import 'package:island/screens/developers/edit_bot.dart';
|
||||
import 'package:island/screens/developers/hub.dart';
|
||||
import 'package:island/screens/developers/new_app.dart';
|
||||
import 'package:island/screens/developers/new_bot.dart';
|
||||
import 'package:island/screens/developers/edit_project.dart';
|
||||
import 'package:island/screens/developers/new_project.dart';
|
||||
import 'package:island/screens/discovery/articles.dart';
|
||||
@@ -570,25 +566,6 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
return const SizedBox.shrink(); // Temporary placeholder
|
||||
},
|
||||
routes: [
|
||||
GoRoute(
|
||||
name: 'developerAppNew',
|
||||
path: 'apps/new',
|
||||
builder:
|
||||
(context, state) => NewCustomAppScreen(
|
||||
publisherName: state.pathParameters['name']!,
|
||||
projectId: state.pathParameters['projectId']!,
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
name: 'developerAppEdit',
|
||||
path: 'apps/:id/edit',
|
||||
builder:
|
||||
(context, state) => EditAppScreen(
|
||||
publisherName: state.pathParameters['name']!,
|
||||
projectId: state.pathParameters['projectId']!,
|
||||
id: state.pathParameters['id']!,
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
name: 'developerAppDetail',
|
||||
path: 'apps/:appId',
|
||||
@@ -599,15 +576,6 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
appId: state.pathParameters['appId']!,
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
name: 'developerBotNew',
|
||||
path: 'bots/new',
|
||||
builder:
|
||||
(context, state) => NewBotScreen(
|
||||
publisherName: state.pathParameters['name']!,
|
||||
projectId: state.pathParameters['projectId']!,
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
name: 'developerBotDetail',
|
||||
path: 'bots/:botId',
|
||||
@@ -618,16 +586,6 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
botId: state.pathParameters['botId']!,
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
name: 'developerBotEdit',
|
||||
path: 'bots/:id/edit',
|
||||
builder:
|
||||
(context, state) => EditBotScreen(
|
||||
publisherName: state.pathParameters['name']!,
|
||||
projectId: state.pathParameters['projectId']!,
|
||||
id: state.pathParameters['id']!,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
@@ -7,7 +7,7 @@ part of 'explore.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$activityListNotifierHash() =>
|
||||
r'a3ad3242f08139bef14a2f0fab6591ce8b3cb9f0';
|
||||
r'77ffc7852feffa5438b56fa26123d453b7c310cf';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:island/models/embed.dart';
|
||||
import 'package:island/models/poll.dart';
|
||||
import 'package:island/services/responsive.dart';
|
||||
import 'package:island/utils/mapping.dart';
|
||||
import 'package:island/widgets/content/embed/link.dart';
|
||||
@@ -54,13 +53,10 @@ class EmbedListWidget extends StatelessWidget {
|
||||
vertical: 8,
|
||||
),
|
||||
child:
|
||||
embedData['poll'] == null
|
||||
? const Text('Poll was not loaded...')
|
||||
embedData['id'] == null
|
||||
? const Text('Poll was unavailable...')
|
||||
: PollSubmit(
|
||||
initialAnswers:
|
||||
embedData['poll']?['user_answer']?['answer'],
|
||||
stats: embedData['poll']?['stats'],
|
||||
poll: SnPollWithStats.fromJson(embedData['poll']),
|
||||
pollId: embedData['id'],
|
||||
onSubmit: (_) {},
|
||||
isReadonly: !isInteractive,
|
||||
isInitiallyExpanded: isFullPost,
|
||||
|
||||
@@ -4,15 +4,15 @@ import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:island/models/poll.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/screens/creators/poll/poll_list.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/poll/poll_stats_widget.dart';
|
||||
|
||||
class PollSubmit extends ConsumerStatefulWidget {
|
||||
const PollSubmit({
|
||||
super.key,
|
||||
required this.poll,
|
||||
required this.pollId,
|
||||
required this.onSubmit,
|
||||
required this.stats,
|
||||
this.initialAnswers,
|
||||
this.onCancel,
|
||||
this.showProgress = true,
|
||||
@@ -20,14 +20,13 @@ class PollSubmit extends ConsumerStatefulWidget {
|
||||
this.isInitiallyExpanded = false,
|
||||
});
|
||||
|
||||
final SnPollWithStats poll;
|
||||
final String pollId;
|
||||
|
||||
/// Callback when user submits all answers. Map questionId -> answer.
|
||||
final void Function(Map<String, dynamic> answers) onSubmit;
|
||||
|
||||
/// Optional initial answers, keyed by questionId.
|
||||
final Map<String, dynamic>? initialAnswers;
|
||||
final Map<String, dynamic>? stats;
|
||||
|
||||
/// Optional cancel callback.
|
||||
final VoidCallback? onCancel;
|
||||
@@ -45,7 +44,7 @@ class PollSubmit extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
late final List<SnPollQuestion> _questions;
|
||||
List<SnPollQuestion>? _questions;
|
||||
int _index = 0;
|
||||
bool _submitting = false;
|
||||
bool _isModifying = false; // New state to track if user is modifying answers
|
||||
@@ -66,14 +65,10 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Ensure questions are ordered by `order`
|
||||
_questions = [...widget.poll.questions]
|
||||
..sort((a, b) => a.order.compareTo(b.order));
|
||||
_answers = Map<String, dynamic>.from(widget.initialAnswers ?? {});
|
||||
// Set initial collapse state based on the parameter
|
||||
_isCollapsed = !widget.isInitiallyExpanded;
|
||||
if (!widget.isReadonly) {
|
||||
_loadCurrentIntoLocalState();
|
||||
// If initial answers are provided, set _isModifying to false initially
|
||||
// so the "Modify" button is shown.
|
||||
if (widget.initialAnswers != null && widget.initialAnswers!.isNotEmpty) {
|
||||
@@ -82,23 +77,25 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
}
|
||||
}
|
||||
|
||||
void _initializeFromPollData(SnPollWithStats poll) {
|
||||
// Initialize answers from poll data if available
|
||||
if (poll.userAnswer != null && poll.userAnswer!.answer.isNotEmpty) {
|
||||
_answers = Map<String, dynamic>.from(poll.userAnswer!.answer);
|
||||
if (!widget.isReadonly && !_isModifying) {
|
||||
_isModifying = false; // Show modify button if user has answered
|
||||
}
|
||||
}
|
||||
_loadCurrentIntoLocalState();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant PollSubmit oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.poll.id != widget.poll.id) {
|
||||
if (oldWidget.pollId != widget.pollId) {
|
||||
_index = 0;
|
||||
_answers = Map<String, dynamic>.from(widget.initialAnswers ?? {});
|
||||
_questions
|
||||
..clear()
|
||||
..addAll(
|
||||
[...widget.poll.questions]
|
||||
..sort((a, b) => a.order.compareTo(b.order)),
|
||||
);
|
||||
if (!widget.isReadonly) {
|
||||
_loadCurrentIntoLocalState();
|
||||
// If poll ID changes, reset modification state
|
||||
_isModifying = false;
|
||||
}
|
||||
// Reset modification state when poll changes
|
||||
_isModifying = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +105,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
SnPollQuestion get _current => _questions[_index];
|
||||
SnPollQuestion get _current => _questions![_index];
|
||||
|
||||
void _loadCurrentIntoLocalState() {
|
||||
final q = _current;
|
||||
@@ -201,7 +198,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _submitToServer() async {
|
||||
Future<void> _submitToServer(SnPollWithStats poll) async {
|
||||
// Persist current question before final submit
|
||||
_persistCurrentAnswer();
|
||||
|
||||
@@ -213,7 +210,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
final dio = ref.read(apiClientProvider);
|
||||
|
||||
await dio.post(
|
||||
'/sphere/polls/${widget.poll.id}/answer',
|
||||
'/sphere/polls/${poll.id}/answer',
|
||||
data: {'answer': _answers},
|
||||
);
|
||||
|
||||
@@ -233,17 +230,17 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
}
|
||||
}
|
||||
|
||||
void _next() {
|
||||
void _next(SnPollWithStats poll) {
|
||||
if (_submitting) return;
|
||||
_persistCurrentAnswer();
|
||||
if (_index < _questions.length - 1) {
|
||||
if (_index < _questions!.length - 1) {
|
||||
setState(() {
|
||||
_index++;
|
||||
_loadCurrentIntoLocalState();
|
||||
});
|
||||
} else {
|
||||
// Final submit to API
|
||||
_submitToServer();
|
||||
_submitToServer(poll);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,41 +258,15 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildHeader(BuildContext context) {
|
||||
Widget _buildHeader(BuildContext context, SnPollWithStats poll) {
|
||||
final q = _current;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (widget.poll.title != null || widget.poll.description != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (widget.poll.title != null)
|
||||
Text(
|
||||
widget.poll.title!,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
if (widget.poll.description != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Text(
|
||||
widget.poll.description!,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).textTheme.bodyMedium?.color?.withOpacity(0.7),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (widget.showProgress &&
|
||||
_isModifying) // Only show progress when modifying
|
||||
Text(
|
||||
'${_index + 1} / ${_questions.length}',
|
||||
'${_index + 1} / ${_questions!.length}',
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
Row(
|
||||
@@ -334,12 +305,18 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStats(BuildContext context, SnPollQuestion q) {
|
||||
return PollStatsWidget(question: q, stats: widget.stats);
|
||||
Widget _buildStats(
|
||||
BuildContext context,
|
||||
SnPollQuestion q,
|
||||
Map<String, dynamic>? stats,
|
||||
) {
|
||||
return PollStatsWidget(question: q, stats: stats);
|
||||
}
|
||||
|
||||
Widget _buildBody(BuildContext context) {
|
||||
if (widget.initialAnswers != null && !widget.isReadonly && !_isModifying) {
|
||||
Widget _buildBody(BuildContext context, SnPollWithStats poll) {
|
||||
final hasUserAnswer =
|
||||
poll.userAnswer != null && poll.userAnswer!.answer.isNotEmpty;
|
||||
if (hasUserAnswer && !widget.isReadonly && !_isModifying) {
|
||||
return const SizedBox.shrink(); // Collapse input fields if already submitted and not modifying
|
||||
}
|
||||
final q = _current;
|
||||
@@ -449,11 +426,13 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildNavBar(BuildContext context) {
|
||||
final isLast = _index == _questions.length - 1;
|
||||
Widget _buildNavBar(BuildContext context, SnPollWithStats poll) {
|
||||
final isLast = _index == _questions!.length - 1;
|
||||
final canProceed = _isCurrentAnswered() && !_submitting;
|
||||
final hasUserAnswer =
|
||||
poll.userAnswer != null && poll.userAnswer!.answer.isNotEmpty;
|
||||
|
||||
if (widget.initialAnswers != null && !_isModifying && !widget.isReadonly) {
|
||||
if (hasUserAnswer && !_isModifying && !widget.isReadonly) {
|
||||
// If poll is submitted and not in modification mode, show "Modify" button
|
||||
return FilledButton.icon(
|
||||
icon: const Icon(Icons.edit),
|
||||
@@ -498,32 +477,32 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
)
|
||||
: Icon(isLast ? Icons.check : Icons.arrow_forward),
|
||||
label: Text(isLast ? 'submit'.tr() : 'next'.tr()),
|
||||
onPressed: canProceed ? _next : null,
|
||||
onPressed: canProceed ? () => _next(poll) : null,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSubmittedView(BuildContext context) {
|
||||
Widget _buildSubmittedView(BuildContext context, SnPollWithStats poll) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (widget.poll.title != null || widget.poll.description != null)
|
||||
if (poll.title != null || poll.description != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (widget.poll.title?.isNotEmpty ?? false)
|
||||
if (poll.title?.isNotEmpty ?? false)
|
||||
Text(
|
||||
widget.poll.title!,
|
||||
poll.title!,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
if (widget.poll.description?.isNotEmpty ?? false)
|
||||
if (poll.description?.isNotEmpty ?? false)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Text(
|
||||
widget.poll.description!,
|
||||
poll.description!,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(
|
||||
context,
|
||||
@@ -534,7 +513,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
],
|
||||
),
|
||||
),
|
||||
for (final q in _questions)
|
||||
for (final q in _questions!)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: Column(
|
||||
@@ -574,7 +553,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildStats(context, q),
|
||||
_buildStats(context, q, poll.stats),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -582,26 +561,26 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildReadonlyView(BuildContext context) {
|
||||
Widget _buildReadonlyView(BuildContext context, SnPollWithStats poll) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (widget.poll.title != null || widget.poll.description != null)
|
||||
if (poll.title != null || poll.description != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (widget.poll.title != null)
|
||||
if (poll.title != null)
|
||||
Text(
|
||||
widget.poll.title!,
|
||||
poll.title!,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
if (widget.poll.description != null)
|
||||
if (poll.description != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: Text(
|
||||
widget.poll.description!,
|
||||
poll.description!,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(
|
||||
context,
|
||||
@@ -612,7 +591,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
],
|
||||
),
|
||||
),
|
||||
for (final q in _questions)
|
||||
for (final q in _questions!)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: Column(
|
||||
@@ -652,7 +631,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildStats(context, q),
|
||||
_buildStats(context, q, poll.stats),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -660,7 +639,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCollapsedView(BuildContext context) {
|
||||
Widget _buildCollapsedView(BuildContext context, SnPollWithStats poll) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -670,20 +649,20 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (widget.poll.title != null)
|
||||
if (poll.title != null)
|
||||
Text(
|
||||
widget.poll.title!,
|
||||
poll.title!,
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
if (widget.poll.description != null)
|
||||
if (poll.description != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 2),
|
||||
child: Text(
|
||||
widget.poll.description!,
|
||||
poll.description!,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(
|
||||
context,
|
||||
@@ -697,7 +676,7 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 2),
|
||||
child: Text(
|
||||
'${_questions.length} question${_questions.length == 1 ? '' : 's'}',
|
||||
'${_questions!.length} question${_questions!.length == 1 ? '' : 's'}',
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(
|
||||
context,
|
||||
@@ -729,111 +708,156 @@ class _PollSubmitState extends ConsumerState<PollSubmit> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_questions.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
final pollAsync = ref.watch(pollWithStatsProvider(widget.pollId));
|
||||
|
||||
// If collapsed, show collapsed view for all states
|
||||
if (_isCollapsed) {
|
||||
return _buildCollapsedView(context);
|
||||
}
|
||||
|
||||
// If poll is already submitted and not in readonly mode, and not in modification mode, show submitted view
|
||||
if (widget.initialAnswers != null && !widget.isReadonly && !_isModifying) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildCollapsedView(context),
|
||||
const SizedBox(height: 8),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
transitionBuilder: (child, anim) {
|
||||
final offset = Tween<Offset>(
|
||||
begin: const Offset(0, -0.1),
|
||||
end: Offset.zero,
|
||||
).animate(CurvedAnimation(parent: anim, curve: Curves.easeOut));
|
||||
final fade = CurvedAnimation(parent: anim, curve: Curves.easeOut);
|
||||
return FadeTransition(
|
||||
opacity: fade,
|
||||
child: SlideTransition(position: offset, child: child),
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
key: const ValueKey('submitted_expanded'),
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [_buildSubmittedView(context), _buildNavBar(context)],
|
||||
return pollAsync.when(
|
||||
loading:
|
||||
() => const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// If poll is in readonly mode, show readonly view
|
||||
if (widget.isReadonly) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildCollapsedView(context),
|
||||
const SizedBox(height: 8),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
transitionBuilder: (child, anim) {
|
||||
final offset = Tween<Offset>(
|
||||
begin: const Offset(0, -0.1),
|
||||
end: Offset.zero,
|
||||
).animate(CurvedAnimation(parent: anim, curve: Curves.easeOut));
|
||||
final fade = CurvedAnimation(parent: anim, curve: Curves.easeOut);
|
||||
return FadeTransition(
|
||||
opacity: fade,
|
||||
child: SlideTransition(position: offset, child: child),
|
||||
);
|
||||
},
|
||||
child: _buildReadonlyView(context),
|
||||
error:
|
||||
(error, stack) => Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text('Failed to load poll: $error'),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
data: (poll) {
|
||||
// Initialize questions when data is available
|
||||
_questions = [...poll.questions]
|
||||
..sort((a, b) => a.order.compareTo(b.order));
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildCollapsedView(context),
|
||||
const SizedBox(height: 8),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
transitionBuilder: (child, anim) {
|
||||
final offset = Tween<Offset>(
|
||||
begin: const Offset(0, -0.1),
|
||||
end: Offset.zero,
|
||||
).animate(CurvedAnimation(parent: anim, curve: Curves.easeOut));
|
||||
final fade = CurvedAnimation(parent: anim, curve: Curves.easeOut);
|
||||
return FadeTransition(
|
||||
opacity: fade,
|
||||
child: SlideTransition(position: offset, child: child),
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
key: const ValueKey('normal_expanded'),
|
||||
// Initialize answers from poll data
|
||||
_initializeFromPollData(poll);
|
||||
|
||||
if (_questions!.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
// If collapsed, show collapsed view for all states
|
||||
if (_isCollapsed) {
|
||||
return _buildCollapsedView(context, poll);
|
||||
}
|
||||
|
||||
// If poll is already submitted and not in readonly mode, and not in modification mode, show submitted view
|
||||
final hasUserAnswer =
|
||||
poll.userAnswer != null && poll.userAnswer!.answer.isNotEmpty;
|
||||
if (hasUserAnswer && !widget.isReadonly && !_isModifying) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildHeader(context),
|
||||
const SizedBox(height: 12),
|
||||
_AnimatedStep(
|
||||
key: ValueKey(_current.id),
|
||||
_buildCollapsedView(context, poll),
|
||||
const SizedBox(height: 8),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
transitionBuilder: (child, anim) {
|
||||
final offset = Tween<Offset>(
|
||||
begin: const Offset(0, -0.1),
|
||||
end: Offset.zero,
|
||||
).animate(
|
||||
CurvedAnimation(parent: anim, curve: Curves.easeOut),
|
||||
);
|
||||
final fade = CurvedAnimation(
|
||||
parent: anim,
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
return FadeTransition(
|
||||
opacity: fade,
|
||||
child: SlideTransition(position: offset, child: child),
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
key: const ValueKey('submitted_expanded'),
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildBody(context),
|
||||
_buildStats(context, _current),
|
||||
_buildSubmittedView(context, poll),
|
||||
_buildNavBar(context, poll),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildNavBar(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// If poll is in readonly mode, show readonly view
|
||||
if (widget.isReadonly) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildCollapsedView(context, poll),
|
||||
const SizedBox(height: 8),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
transitionBuilder: (child, anim) {
|
||||
final offset = Tween<Offset>(
|
||||
begin: const Offset(0, -0.1),
|
||||
end: Offset.zero,
|
||||
).animate(
|
||||
CurvedAnimation(parent: anim, curve: Curves.easeOut),
|
||||
);
|
||||
final fade = CurvedAnimation(
|
||||
parent: anim,
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
return FadeTransition(
|
||||
opacity: fade,
|
||||
child: SlideTransition(position: offset, child: child),
|
||||
);
|
||||
},
|
||||
child: _buildReadonlyView(context, poll),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildCollapsedView(context, poll),
|
||||
const SizedBox(height: 8),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
transitionBuilder: (child, anim) {
|
||||
final offset = Tween<Offset>(
|
||||
begin: const Offset(0, -0.1),
|
||||
end: Offset.zero,
|
||||
).animate(CurvedAnimation(parent: anim, curve: Curves.easeOut));
|
||||
final fade = CurvedAnimation(
|
||||
parent: anim,
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
return FadeTransition(
|
||||
opacity: fade,
|
||||
child: SlideTransition(position: offset, child: child),
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
key: const ValueKey('normal_expanded'),
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildHeader(context, poll),
|
||||
const SizedBox(height: 12),
|
||||
_AnimatedStep(
|
||||
key: ValueKey(_current.id),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildBody(context, poll),
|
||||
_buildStats(context, _current, poll.stats),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildNavBar(context, poll),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user