[READ ONLY MIRROR] Open Source TikTok alternative built on AT Protocol github.com/sprksocial/client
flutter atproto video dart
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

Update comment_input.dart (#19)

authored by

C3B and committed by
GitHub
6f17f8d9 23364515

+134 -126
+134 -126
lib/widgets/comments/comment_input.dart
··· 213 213 final inputBackgroundColor = widget.isDarkMode ? AppColors.deepPurple.withAlpha(128) : AppColors.lightLavender.withAlpha(77); 214 214 215 215 return Container( 216 - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), 216 + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), 217 217 decoration: BoxDecoration(color: backgroundColor, border: Border(top: BorderSide(color: borderColor, width: 0.5))), 218 218 child: Column( 219 219 crossAxisAlignment: CrossAxisAlignment.start, ··· 222 222 // Emoji Picker is always displayed at the top 223 223 EmojiPicker(onEmojiSelected: _insertEmoji, isDarkMode: widget.isDarkMode), 224 224 225 - const SizedBox(height: 8), 225 + const SizedBox(height: 16), 226 226 227 - if (widget.replyingToUsername != null) _buildReplyingToNotice(inputBackgroundColor, borderColor, textColor), 227 + if (widget.replyingToUsername != null) 228 + Padding( 229 + padding: const EdgeInsets.only(bottom: 12.0), 230 + child: _buildReplyingToNotice(inputBackgroundColor, borderColor, textColor), 231 + ), 228 232 229 - Row( 230 - crossAxisAlignment: CrossAxisAlignment.end, 231 - children: [ 232 - _buildUserAvatar(borderColor), 233 - const SizedBox(width: 12), 234 - Expanded(child: _buildTextField(inputBackgroundColor, borderColor, textColor, placeholderColor)), 235 - const SizedBox(width: 8), 236 - _buildAttachmentButton(inputBackgroundColor, borderColor, textColor), 237 - ], 233 + // Updated input row with centered alignment 234 + Container( 235 + decoration: BoxDecoration( 236 + color: inputBackgroundColor, 237 + borderRadius: BorderRadius.circular(24), 238 + border: Border.all(color: borderColor, width: 0.5), 239 + ), 240 + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), 241 + child: Row( 242 + crossAxisAlignment: CrossAxisAlignment.center, 243 + children: [ 244 + _buildUserAvatar(borderColor), 245 + const SizedBox(width: 8), 246 + _buildAttachmentButton(borderColor, textColor), 247 + const SizedBox(width: 8), 248 + Expanded(child: _buildTextField(textColor, placeholderColor)), 249 + ], 250 + ), 238 251 ), 239 252 240 253 // Selected Images Preview (only show if images are selected) 241 - if (_selectedImages.isNotEmpty) _buildSelectedImagesPreview(borderColor), 254 + if (_selectedImages.isNotEmpty) 255 + Padding( 256 + padding: const EdgeInsets.only(top: 12.0), 257 + child: _buildSelectedImagesPreview(borderColor), 258 + ), 242 259 ], 243 260 ), 244 261 ); ··· 247 264 Widget _buildReplyingToNotice(Color inputBackgroundColor, Color borderColor, Color textColor) { 248 265 return Container( 249 266 padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), 250 - margin: const EdgeInsets.only(bottom: 8), 251 267 decoration: BoxDecoration( 252 268 color: inputBackgroundColor, 253 269 borderRadius: BorderRadius.circular(8), ··· 274 290 decoration: BoxDecoration( 275 291 color: AppColors.accent, 276 292 shape: BoxShape.circle, 277 - border: Border.all(color: widget.isDarkMode ? AppColors.deepPurple : AppColors.lightLavender, width: 1), 293 + border: Border.all(color: borderColor, width: 1), 294 + boxShadow: [ 295 + BoxShadow( 296 + color: AppColors.accent.withOpacity(0.2), 297 + blurRadius: 4, 298 + spreadRadius: 1, 299 + ), 300 + ], 278 301 ), 279 302 child: const Center( 280 303 child: Text( ··· 285 308 ); 286 309 } 287 310 288 - Widget _buildTextField(Color inputBackgroundColor, Color borderColor, Color textColor, Color placeholderColor) { 311 + Widget _buildTextField(Color textColor, Color placeholderColor) { 289 312 String hint = 'Add a comment...'; 290 313 if (widget.replyingToUsername != null) { 291 314 hint = 'Reply to ${widget.replyingToUsername}...'; ··· 297 320 controller: _textController, 298 321 focusNode: widget.focusNode, 299 322 decoration: InputDecoration( 300 - hintText: hint, // Updated hint logic 323 + hintText: hint, 301 324 hintStyle: TextStyle(color: placeholderColor, fontSize: 14), 302 - filled: true, 303 - fillColor: inputBackgroundColor, 304 - contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), 305 - border: OutlineInputBorder( 306 - borderRadius: BorderRadius.circular(20), 307 - borderSide: BorderSide(color: borderColor, width: 0.5), 308 - ), 309 - enabledBorder: OutlineInputBorder( 310 - borderRadius: BorderRadius.circular(20), 311 - borderSide: BorderSide(color: borderColor, width: 0.5), 312 - ), 313 - focusedBorder: OutlineInputBorder( 314 - borderRadius: BorderRadius.circular(20), 315 - borderSide: BorderSide(color: borderColor, width: 0.5), 316 - ), 325 + filled: false, 326 + isDense: true, 327 + contentPadding: const EdgeInsets.symmetric(horizontal: 4, vertical: 10), 328 + border: InputBorder.none, 329 + enabledBorder: InputBorder.none, 330 + focusedBorder: InputBorder.none, 317 331 suffixIcon: 318 - _isPosting // Show progress if posting OR specifically uploading images 332 + _isPosting 319 333 ? Container( 320 - margin: const EdgeInsets.all(10), 334 + margin: const EdgeInsets.all(8), 321 335 width: 20, 322 336 height: 20, 323 337 child: CircularProgressIndicator( 324 338 strokeWidth: 2, 325 - valueColor: AlwaysStoppedAnimation<Color>(AppColors.primary), // Use valueColor 339 + valueColor: AlwaysStoppedAnimation<Color>(AppColors.primary), 326 340 ), 327 341 ) 328 342 : IconButton( 329 343 icon: Icon(FluentIcons.send_24_filled, size: 20, color: _canSubmit ? AppColors.primary : placeholderColor), 330 - onPressed: _canSubmit ? _submitComment : null, // Controlled by _canSubmit 344 + onPressed: _canSubmit ? _submitComment : null, 331 345 ), 332 346 ), 333 347 style: TextStyle(color: textColor, fontSize: 14), 334 348 maxLines: 5, 335 349 minLines: 1, 336 350 cursorColor: AppColors.primary, 337 - enabled: !_isPosting, // Disable field while posting 351 + enabled: !_isPosting, 338 352 ); 339 353 } 340 354 341 - Widget _buildAttachmentButton(Color inputBackgroundColor, Color borderColor, Color textColor) { 342 - final bool canAddMoreImages = _selectedImages.length < 4; // Example limit 355 + Widget _buildAttachmentButton(Color borderColor, Color textColor) { 356 + final bool canAddMoreImages = _selectedImages.length < 4; 343 357 final bool enabled = !_isPosting && canAddMoreImages; 344 358 345 359 return IconButton( 346 360 padding: EdgeInsets.zero, 347 - constraints: const BoxConstraints(minWidth: 34, minHeight: 34), 348 - onPressed: enabled ? _pickImages : null, // Trigger image picker 361 + constraints: const BoxConstraints(minWidth: 32, minHeight: 32), 362 + onPressed: enabled ? _pickImages : null, 349 363 tooltip: enabled ? 'Add images (up to 4)' : (_isPosting ? 'Posting...' : 'Maximum images reached'), 350 - icon: Container( 351 - width: 34, 352 - height: 34, 353 - decoration: BoxDecoration( 354 - color: inputBackgroundColor, 355 - shape: BoxShape.circle, 356 - border: Border.all(color: borderColor, width: 0.5), 357 - ), 358 - child: Icon( 359 - // Change icon based on state if desired, e.g., FluentIcons.image_add_24_regular 360 - FluentIcons.add_24_regular, 361 - size: 18, 362 - color: enabled ? textColor : textColor.withOpacity(0.5), // Dim if disabled 363 - ), 364 + icon: Icon( 365 + FluentIcons.image_24_regular, 366 + size: 20, 367 + color: enabled ? AppColors.primary : textColor.withOpacity(0.5), 364 368 ), 365 369 ); 366 370 } 367 371 368 372 // New widget to display selected image thumbnails 369 373 Widget _buildSelectedImagesPreview(Color borderColor) { 370 - return Padding( 371 - padding: const EdgeInsets.only(top: 12.0, left: 44), // Align with text field start 372 - child: SizedBox( 373 - height: 64, // Adjust height as needed 374 - child: ListView.builder( 375 - scrollDirection: Axis.horizontal, 376 - itemCount: _selectedImages.length, 377 - itemBuilder: (context, index) { 378 - final imageFile = _selectedImages[index]; 379 - final alt = _altTexts[imageFile.path] ?? ''; 380 - return Padding( 381 - padding: const EdgeInsets.only(right: 8.0), 382 - child: Stack( 383 - alignment: Alignment.bottomRight, 384 - children: [ 385 - // Image Thumbnail 386 - Container( 387 - width: 64, 388 - height: 64, 389 - decoration: BoxDecoration( 390 - borderRadius: BorderRadius.circular(8), 391 - border: Border.all(color: borderColor, width: 0.5), 392 - image: DecorationImage(image: FileImage(File(imageFile.path)), fit: BoxFit.cover), 393 - ), 374 + return SizedBox( 375 + height: 72, 376 + child: ListView.builder( 377 + scrollDirection: Axis.horizontal, 378 + itemCount: _selectedImages.length, 379 + itemBuilder: (context, index) { 380 + final imageFile = _selectedImages[index]; 381 + final alt = _altTexts[imageFile.path] ?? ''; 382 + return Padding( 383 + padding: const EdgeInsets.only(right: 8.0), 384 + child: Stack( 385 + alignment: Alignment.bottomRight, 386 + children: [ 387 + // Image Thumbnail with rounded corners and shadow 388 + Container( 389 + width: 72, 390 + height: 72, 391 + decoration: BoxDecoration( 392 + borderRadius: BorderRadius.circular(12), 393 + border: Border.all(color: borderColor, width: 0.5), 394 + boxShadow: [ 395 + BoxShadow( 396 + color: Colors.black.withOpacity(0.1), 397 + blurRadius: 4, 398 + offset: const Offset(0, 2), 399 + ), 400 + ], 401 + image: DecorationImage(image: FileImage(File(imageFile.path)), fit: BoxFit.cover), 394 402 ), 395 - // ALT Button (bottom right) 396 - Positioned( 397 - bottom: 4, 398 - right: 4, 399 - child: Material( 400 - color: Colors.black.withOpacity(0.5), 403 + ), 404 + // ALT Button (bottom right) 405 + Positioned( 406 + bottom: 4, 407 + right: 4, 408 + child: Material( 409 + color: Colors.black.withOpacity(0.5), 410 + borderRadius: BorderRadius.circular(8), 411 + child: InkWell( 412 + onTap: () async { 413 + final result = await showDialog<String>( 414 + context: context, 415 + builder: (context) => AltTextEditorDialog(imageFile: imageFile, initialAltText: alt), 416 + ); 417 + if (result != null) { 418 + setState(() { 419 + _altTexts[imageFile.path] = result.trim(); 420 + }); 421 + } 422 + }, 401 423 borderRadius: BorderRadius.circular(8), 402 - child: InkWell( 403 - onTap: () async { 404 - final result = await showDialog<String>( 405 - context: context, 406 - builder: (context) => AltTextEditorDialog(imageFile: imageFile, initialAltText: alt), 407 - ); 408 - if (result != null) { 409 - setState(() { 410 - _altTexts[imageFile.path] = result.trim(); 411 - }); 412 - } 413 - }, 414 - borderRadius: BorderRadius.circular(8), 415 - child: Padding( 416 - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), 417 - child: Row( 418 - children: [ 419 - Icon(FluentIcons.image_alt_text_20_regular, color: Colors.white, size: 14), 420 - const SizedBox(width: 2), 421 - const Text('ALT', style: TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)), 422 - ], 423 - ), 424 + child: Padding( 425 + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), 426 + child: Row( 427 + children: [ 428 + Icon(FluentIcons.image_alt_text_20_regular, color: Colors.white, size: 14), 429 + const SizedBox(width: 2), 430 + const Text('ALT', style: TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)), 431 + ], 424 432 ), 425 433 ), 426 434 ), 427 435 ), 428 - // Remove Button (top right) 429 - Positioned( 430 - top: 4, 431 - right: 4, 432 - child: Material( 433 - color: Colors.black.withOpacity(0.5), 434 - shape: const CircleBorder(), 435 - child: InkWell( 436 - onTap: () => _removeImage(index), 437 - customBorder: const CircleBorder(), 438 - child: Container( 439 - padding: const EdgeInsets.all(2), 440 - child: const Icon(FluentIcons.dismiss_16_filled, color: Colors.white, size: 12), 441 - ), 436 + ), 437 + // Remove Button (top right) 438 + Positioned( 439 + top: 4, 440 + right: 4, 441 + child: Material( 442 + color: Colors.black.withOpacity(0.5), 443 + shape: const CircleBorder(), 444 + child: InkWell( 445 + onTap: () => _removeImage(index), 446 + customBorder: const CircleBorder(), 447 + child: Container( 448 + padding: const EdgeInsets.all(2), 449 + child: const Icon(FluentIcons.dismiss_16_filled, color: Colors.white, size: 12), 442 450 ), 443 451 ), 444 452 ), 445 - ], 446 - ), 447 - ); 448 - }, 449 - ), 453 + ), 454 + ], 455 + ), 456 + ); 457 + }, 450 458 ), 451 459 ); 452 460 }