1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2025-06-19 08:15:16 +00:00

Start to fix null safety migration errors

This commit is contained in:
2022-03-10 19:39:57 +01:00
parent ab2c5da0da
commit 3a997cdc56
258 changed files with 2879 additions and 2912 deletions

View File

@ -5,6 +5,6 @@ import 'package:comunic/helpers/account_helper.dart';
/// @author Pierre HUBERT
/// Get the ID of the current user
int userID() {
int? userID() {
return AccountHelper.getCurrentUserID();
}

View File

@ -3,8 +3,8 @@
/// @author Pierre Hubert
/// Casting helper
T cast<T>(dynamic val) => val is T ? val : null;
T? cast<T>(dynamic val) => val is T ? val : null;
/// Turn null and "null" into ""
String nullToEmpty(String input) =>
String nullToEmpty(String? input) =>
input == null || input == "null" ? "" : input;

View File

@ -6,33 +6,33 @@ import 'package:flutter/material.dart';
/// This callback return null if the text has to be left as is or a TextSpan
/// if it has been sub parsed...
typedef ParseCallBack = List<InlineSpan> Function(TextStyle, String);
typedef ParseCallBack = List<InlineSpan> Function(TextStyle, String?);
class BBCodeParsedWidget extends StatelessWidget {
final _Element _content;
final ParseCallBack parseCallback;
final _Element? _content;
final ParseCallBack? parseCallback;
BBCodeParsedWidget({@required String text, this.parseCallback})
BBCodeParsedWidget({required String text, this.parseCallback})
: assert(text != null),
_content = _parse(text);
_printRecur(_Element el, {int pos = 0}) {
String str;
str = "".padLeft(pos, "*");
if (el.text != null) print(str + el.text);
el.children.forEach((f) => _printRecur(f, pos: pos + 1));
if (el.text != null) print(str + el.text!);
el.children.forEach((f) => _printRecur(f!, pos: pos + 1));
}
@override
Widget build(BuildContext context) {
_printRecur(_content);
_printRecur(_content!);
return RichText(
text: _content.toTextSpan(context, parseCallback),
text: _content!.toTextSpan(context, parseCallback),
);
}
/// Initialize parsing
static _Element _parse(String text) {
static _Element? _parse(String text) {
try {
return _parseRecur(
text: text,
@ -48,19 +48,19 @@ class BBCodeParsedWidget extends StatelessWidget {
/// Recursive parsing
static _ElementRecur _parseRecur({
@required String text,
@required _ElementStyle style,
@required int pos,
String parentTag,
required String text,
required _ElementStyle style,
required int pos,
String? parentTag,
}) {
_Element el = _Element(style: style.clone());
int lastBeginPos = pos;
int? lastBeginPos = pos;
int childNumber = 0;
bool stop = false;
while (!stop && pos < text.length) {
while (!stop && pos! < text.length) {
//Go to next stop
while (!stop && pos < text.length) {
while (!stop && pos! < text.length) {
if (text[pos] == '[') break;
pos++;
}
@ -68,7 +68,7 @@ class BBCodeParsedWidget extends StatelessWidget {
//Check for text with default style to apply
if (lastBeginPos != pos)
el.children.add(_Element(
style: style.clone(), text: text.substring(lastBeginPos, pos)));
style: style.clone(), text: text.substring(lastBeginPos!, pos)));
//Check if the [ tag is alone
if (pos == text.length)
@ -89,7 +89,7 @@ class BBCodeParsedWidget extends StatelessWidget {
// Prepare tag detection
final closeBrace = text.indexOf("]", pos);
String tag = text.substring(pos + 1, closeBrace);
String arg;
String? arg;
final newStyle = style.clone();
//Check for argument
@ -126,7 +126,7 @@ class BBCodeParsedWidget extends StatelessWidget {
}
/// Pre-parse tag
static void _preParseTag(String tag, _ElementStyle style, [String arg]) {
static void _preParseTag(String tag, _ElementStyle style, [String? arg]) {
switch (tag) {
// Bold
case "b":
@ -153,9 +153,9 @@ class BBCodeParsedWidget extends StatelessWidget {
assert(arg != null);
style.color = Color.fromARGB(
255,
int.tryParse(arg.substring(1, 3), radix: 16),
int.tryParse(arg.substring(3, 5), radix: 16),
int.tryParse(arg.substring(5, 7), radix: 16),
int.tryParse(arg!.substring(1, 3), radix: 16)!,
int.tryParse(arg.substring(3, 5), radix: 16)!,
int.tryParse(arg.substring(5, 7), radix: 16)!,
);
break;
@ -167,18 +167,18 @@ class BBCodeParsedWidget extends StatelessWidget {
}
/// Post-parse tag
static void _postParseTag(String tag, _Element el,
{String arg, String parentTag, int childNumber}) {
static void _postParseTag(String tag, _Element? el,
{String? arg, String? parentTag, int? childNumber}) {
// List container
if (tag == "ul" || tag == "ol")
el.children.insert(0, _Element(style: el.style, text: "\n"));
el!.children.insert(0, _Element(style: el.style, text: "\n"));
// List children
if (tag == "li") {
el.children.add(_Element(style: el.style, text: "\n"));
el!.children.add(_Element(style: el.style, text: "\n"));
if (parentTag == "ol")
el.children.insert(
0, _Element(style: el.style, text: " ${childNumber + 1}. "));
0, _Element(style: el.style, text: " ${childNumber! + 1}. "));
else
el.children.insert(0, _Element(style: el.style, text: " \u2022 "));
}
@ -187,20 +187,20 @@ class BBCodeParsedWidget extends StatelessWidget {
/// An element's style
class _ElementStyle {
TextDecoration decoration;
FontWeight fontWeight;
FontStyle fontStyle;
Color color;
TextDecoration? decoration;
FontWeight? fontWeight;
FontStyle? fontStyle;
Color? color;
/// Generate an empty style
_ElementStyle.empty();
/// Construct an instance of this element
_ElementStyle(
{@required this.decoration,
@required this.fontWeight,
@required this.fontStyle,
@required this.color});
{required this.decoration,
required this.fontWeight,
required this.fontStyle,
required this.color});
/// Clone this style
_ElementStyle clone() {
@ -213,7 +213,7 @@ class _ElementStyle {
/// Generate corresponding TextStyle
TextStyle toTextStyle(BuildContext context) {
return Theme.of(context).textTheme.bodyText2.copyWith(
return Theme.of(context).textTheme.bodyText2!.copyWith(
decoration: decoration,
fontWeight: fontWeight,
fontStyle: fontStyle,
@ -224,14 +224,14 @@ class _ElementStyle {
/// An element
class _Element {
/// Note : if text is not null, children must be empty !!!
String text;
String? text;
final _ElementStyle style;
final List<_Element> children = [];
final List<_Element?> children = [];
_Element({@required this.style, this.text});
_Element({required this.style, this.text});
/// Turn this element into a TextSpan
TextSpan toTextSpan(BuildContext context, ParseCallBack parseCallback) {
TextSpan toTextSpan(BuildContext context, ParseCallBack? parseCallback) {
assert(text == null || children.length == 0);
final generatedStyle = this.style.toTextStyle(context);
@ -250,14 +250,14 @@ class _Element {
text: text,
style: generatedStyle,
children:
children.map((f) => f.toTextSpan(context, parseCallback)).toList(),
children.map((f) => f!.toTextSpan(context, parseCallback)).toList(),
);
}
}
class _ElementRecur {
final _Element el;
final _Element? el;
final int finalPos;
const _ElementRecur({this.el, this.finalPos});
const _ElementRecur({this.el, required this.finalPos});
}

View File

@ -10,5 +10,5 @@ import 'intl_utils.dart';
void copyToClipboard(BuildContext context, String content) {
FlutterClipboard.copy(content);
snack(context, tr("'%1%' copied to clipboard!", args: {"1": content}));
snack(context, tr("'%1%' copied to clipboard!", args: {"1": content})!);
}

View File

@ -4,7 +4,7 @@ import 'dart:ui';
///
/// @author Pierre Hubert
String colorToHex(Color color) {
String colorToHex(Color? color) {
if (color == null) return "";
return (color.red.toRadixString(16).padLeft(2, '0') +

View File

@ -9,17 +9,17 @@ import 'package:flutter/material.dart';
/// @author Pierre HUBERT
/// Open a private conversation with a given [userID]
Future<bool> openPrivateConversation(BuildContext context, int userID) async {
Future<bool> openPrivateConversation(BuildContext context, int? userID) async {
try {
final convID = await ConversationsHelper().getPrivate(userID);
// Open the conversation
MainController.of(context).openConversationById(convID);
MainController.of(context)!.openConversationById(convID);
return true;
} catch (e, s) {
print("Failed to find private conversation! $e => $s");
showSimpleSnack(context, tr("Could not find a private conversation!"));
showSimpleSnack(context, tr("Could not find a private conversation!")!);
return false;
}
}

View File

@ -10,7 +10,7 @@ int time() {
return (DateTime.now().millisecondsSinceEpoch / 1000).floor();
}
String diffTimeToStr(int amount) {
String? diffTimeToStr(int amount) {
if (amount < 0) amount = 0;
// Seconds
@ -45,7 +45,7 @@ String diffTimeToStr(int amount) {
: tr("%years% years", args: {"years": years.toString()});
}
String diffTimeFromNowToStr(int date) {
String? diffTimeFromNowToStr(int date) {
return diffTimeToStr(time() - date);
}
@ -59,7 +59,7 @@ String formatDuration(Duration d) =>
"${d.inMinutes < 10 ? "0" + d.inMinutes.toString() : d.inMinutes.toString()}:${d.inSeconds % 60 < 10 ? "0" + (d.inSeconds % 60).toString() : (d.inSeconds % 60).toString()}";
/// Compare two [DateTime] on their date
bool isSameDate(DateTime one, DateTime other) {
bool isSameDate(DateTime one, DateTime? other) {
if (other == null) return false;
return one.year == other.year &&
one.month == other.month &&

View File

@ -16,8 +16,8 @@ import '../ui/dialogs/pick_file_dialog.dart';
/// Ask the user to choose an image, either from the gallery or using the camera
///
/// Throws an exception null in case of failure
Future<BytesFile> pickImage(BuildContext context,
{CropAspectRatio aspectRatio}) async {
Future<BytesFile?> pickImage(BuildContext context,
{CropAspectRatio? aspectRatio}) async {
return await showPickFileDialog(
context: context,
allowedMimeTypes: ["image/png", "image/jpeg", "image/gif"],

View File

@ -10,11 +10,10 @@ import 'package:random_string/random_string.dart';
///
/// @author Pierre Hubert
// Based on https://stackoverflow.com/a/63215502/3781411
Future<Uint8List> svgToPng(BuildContext context, String svgString,
{int svgWidth, int svgHeight}) async {
DrawableRoot svgDrawableRoot = await svg.fromSvgString(svgString, null);
{int? svgWidth, int? svgHeight}) async {
DrawableRoot svgDrawableRoot = await svg.fromSvgString(svgString, "svg");
// to have a nice rendering it is important to have the exact original height and width,
// the easier way to retrieve it is directly from the svg string
@ -40,7 +39,7 @@ Future<Uint8List> svgToPng(BuildContext context, String svgString,
// Convert to ui.Image. toImage() takes width and height as parameters
// you need to find the best size to suit your needs and take into account the screen DPI
final image = await picture.toImage(width.toInt(), height.toInt());
ByteData bytes = await image.toByteData(format: ImageByteFormat.png);
ByteData bytes = (await image.toByteData(format: ImageByteFormat.png))!;
return bytes.buffer.asUint8List();
}

View File

@ -12,7 +12,7 @@ bool legacyValidatePassword(String s) => s.length > 3;
bool validateEmail(String value) {
Pattern pattern =
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regex = new RegExp(pattern);
RegExp regex = new RegExp(pattern as String);
return regex.hasMatch(value);
}

View File

@ -8,8 +8,8 @@ import 'package:intl/intl_standalone.dart';
///
/// @author Pierre HUBERT
String _currLang;
Map<String, Map<String, String>> translations;
String? _currLang;
late Map<String, Map<String, String>> translations;
/// Initialize translations system
///
@ -35,22 +35,22 @@ Future<void> initTranslations() async {
///
/// Then apply the list of [args] to the string, each argument name is
/// surrounded by '%'
String tr(String string, {Map<String, String> args}) {
String? tr(String? string, {Map<String, String?>? args}) {
// Check if a translation is available
if (_currLang != null &&
translations.containsKey(_currLang) &&
translations[_currLang].containsKey(string))
string = translations[_currLang][string];
translations[_currLang!]!.containsKey(string))
string = translations[_currLang!]![string!];
//Apply arguments
if (args != null)
args.forEach((key, value) => string = string.replaceAll("%$key%", value));
args.forEach((key, value) => string = string!.replaceAll("%$key%", value!));
return string;
}
/// Get current lang, in format aa_BB
String/*!*/ get lang => _currLang != null ? _currLang : "en_US";
String get lang => _currLang != null ? _currLang! : "en_US";
/// Get short lang format, in format aa

View File

@ -11,20 +11,20 @@ import 'package:flutter/material.dart';
/// Pop a page
void popPage(BuildContext context) {
MainController.of(context).popPage();
MainController.of(context)!.popPage();
}
/// Open the page of a user
void openUserPage({@required int userID, @required BuildContext context}) {
void openUserPage({required int userID, required BuildContext context}) {
assert(userID != null);
assert(context != null);
MainController.of(context).openUserPage(userID);
MainController.of(context)!.openUserPage(userID);
}
/// Open a post in full screen
void openPostFullScreen(int postID, BuildContext context) {
MainController.of(context).push(SinglePostRoute(postID: postID));
MainController.of(context)!.push(SinglePostRoute(postID: postID));
}
/// Open a virtual directory
@ -36,22 +36,22 @@ void openVirtualDirectory(BuildContext context, String directory) async {
switch (result.type) {
case VirtualDirectoryType.USER:
openUserPage(context: context, userID: result.id);
openUserPage(context: context, userID: result.id!);
break;
case VirtualDirectoryType.GROUP:
MainController.of(context).openGroup(result.id);
MainController.of(context)!.openGroup(result.id!);
break;
case VirtualDirectoryType.NONE:
await showDialog(
context: context,
builder: (c) => AlertDialog(
title: Text(tr("Error")),
content: Text(tr("Could not find related resource!")),
title: Text(tr("Error")!),
content: Text(tr("Could not find related resource!")!),
actions: <Widget>[
MaterialButton(
child: Text(tr("OK")),
child: Text(tr("OK")!),
onPressed: () => Navigator.of(c).pop(),
)
],
@ -61,6 +61,6 @@ void openVirtualDirectory(BuildContext context, String directory) async {
} catch (e, stack) {
print(e);
print(stack);
showSimpleSnack(context, tr("Could not search virtual directory!"));
showSimpleSnack(context, tr("Could not search virtual directory!")!);
}
}

View File

@ -23,7 +23,7 @@ Widget buildCenteredProgressBar() {
/// Build and return a full loading page
Widget buildLoadingPage({
bool showAppBar = false,
String routeTitle,
String? routeTitle,
}) {
return Scaffold(
appBar: showAppBar
@ -36,8 +36,8 @@ Widget buildLoadingPage({
}
/// Build and return an error card
Widget buildErrorCard(String message,
{List<Widget> actions, bool hide = false}) {
Widget buildErrorCard(String? message,
{List<Widget>? actions, bool hide = false}) {
if (hide) return Container();
return Theme(
@ -59,7 +59,7 @@ Widget buildErrorCard(String message,
),
Flexible(
child: Text(
message,
message!,
maxLines: null,
),
),
@ -74,9 +74,9 @@ Widget buildErrorCard(String message,
}
/// Show an image with a given [url] in full screen
void showImageFullScreen(BuildContext context, String url) {
void showImageFullScreen(BuildContext context, String? url) {
Navigator.of(context).push(MaterialPageRoute(builder: (c) {
return FullScreenImageRoute(url);
return FullScreenImageRoute(url!);
}));
}
@ -92,12 +92,12 @@ void snack(BuildContext context, String message) {
/// Show an alert dialog to ask the user to enter a string
///
/// Returns entered string if the dialog is confirmed, null else
Future<String> askUserString({
@required BuildContext context,
@required String title,
@required String message,
@required String defaultValue,
@required String hint,
Future<String?> askUserString({
required BuildContext context,
required String title,
required String message,
required String defaultValue,
required String hint,
int maxLength = 200,
int minLength = 1,
}) async {
@ -135,13 +135,13 @@ class _InputTextDialog extends StatefulWidget {
final String hint;
const _InputTextDialog({
Key key,
@required this.title,
@required this.message,
@required this.controller,
@required this.maxLength,
@required this.minLength,
@required this.hint,
Key? key,
required this.title,
required this.message,
required this.controller,
required this.maxLength,
required this.minLength,
required this.hint,
}) : super(key: key);
@override
@ -172,11 +172,11 @@ class __InputTextDialogState extends State<_InputTextDialog> {
),
actions: <Widget>[
TextButton(
child: Text(tr("Cancel").toUpperCase()),
child: Text(tr("Cancel")!.toUpperCase()),
onPressed: () => Navigator.pop(c, false),
),
TextButton(
child: Text(tr("OK")),
child: Text(tr("OK")!),
onPressed: widget.controller.text.length >= widget.minLength
? () => Navigator.pop(c, true)
: null,
@ -189,9 +189,9 @@ class __InputTextDialogState extends State<_InputTextDialog> {
///
/// Return value of this function is never null
Future<bool> showConfirmDialog({
@required BuildContext context,
String title,
@required String message,
required BuildContext context,
String? title,
required String? message,
}) async {
if (title == null) title = tr("Confirm operation");
@ -202,17 +202,17 @@ Future<bool> showConfirmDialog({
final result = await showDialog<bool>(
context: ctx,
builder: (c) => AlertDialog(
title: Text(title),
content: Text(message),
title: Text(title!),
content: Text(message!),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, false),
child: Text(tr("Cancel").toUpperCase()),
child: Text(tr("Cancel")!.toUpperCase()),
),
TextButton(
onPressed: () => Navigator.pop(context, true),
child: Text(
tr("Confirm").toUpperCase(),
tr("Confirm")!.toUpperCase(),
style: TextStyle(color: Colors.red),
),
),
@ -224,15 +224,15 @@ Future<bool> showConfirmDialog({
/// Show a simple alert dialog
Future<void> showAlert({
@required BuildContext context,
@required String message,
String title,
required BuildContext context,
required String? message,
String? title,
}) async =>
await showDialog(
context: context,
builder: (c) => AlertDialog(
title: title == null ? null : Text(title),
content: Text(message),
content: Text(message!),
actions: [CancelDialogButton()],
));
@ -240,28 +240,28 @@ Future<void> showAlert({
/// text has already been entered by the user
Widget smartInputCounterWidgetBuilder(
BuildContext context, {
@required int currentLength,
@required int maxLength,
@required bool isFocused,
required int currentLength,
required int? maxLength,
required bool isFocused,
}) =>
currentLength > 0 ? Text("$currentLength/$maxLength") : Container();
/// Parse an HTML String to decode special characters
String htmlDecodeCharacters(String input) {
String htmlDecodeCharacters(String? input) {
if (input == null || input == "") return "";
return parse(input).documentElement.text;
return parse(input).documentElement!.text;
}
const darkAccentColor = Colors.white70;
const darkerAccentColor = Colors.white30;
/// Check out whether dark theme is enabled or not
bool darkTheme() => preferences().getBool(PreferencesKeyList.ENABLE_DARK_THEME);
bool darkTheme() => preferences()!.getBool(PreferencesKeyList.ENABLE_DARK_THEME);
/// Check out whether we use tablet mode or not
bool isTablet(BuildContext context) =>
!preferences().getBool(PreferencesKeyList.FORCE_MOBILE_MODE) &&
!preferences()!.getBool(PreferencesKeyList.FORCE_MOBILE_MODE) &&
MediaQuery.of(context).size.width >= 1024;
/// Show about Comunic dialog
@ -272,7 +272,7 @@ void showAboutAppDialog(BuildContext context) {
children: <Widget>[
Text(
tr(config().appQuickDescription) ??
tr("Comunic is a free and OpenSource social network that respect your privacy."),
tr("Comunic is a free and OpenSource social network that respect your privacy.")!,
textAlign: TextAlign.center,
),
SizedBox(
@ -287,7 +287,7 @@ void showAboutAppDialog(BuildContext context) {
/// Apply new theme settings
void applyNewThemeSettings(BuildContext context) =>
context.findAncestorStateOfType<ComunicApplicationState>().refresh();
context.findAncestorStateOfType<ComunicApplicationState>()!.refresh();
/// Parse emojies
String parseEmojies(String input) => EmojiParser().emojify(input);

View File

@ -2,7 +2,6 @@ import 'dart:io';
import 'package:comunic/models/api_request.dart';
import 'package:comunic/utils/log_utils.dart';
import 'package:flutter/material.dart';
import 'package:video_thumbnail/video_thumbnail.dart';
import 'files_utils.dart';
@ -12,16 +11,16 @@ import 'files_utils.dart';
/// @author Pierre Hubert
/// Generate a thumbnail for a video. In case of failure, return null
Future<BytesFile> generateVideoThumbnail({
@required BytesFile videoFile,
int maxWidth,
Future<BytesFile?> generateVideoThumbnail({
required BytesFile videoFile,
required int maxWidth,
}) async {
File file;
File? file;
try {
file = await generateTemporaryFile();
await file.writeAsBytes(videoFile.bytes);
await file.writeAsBytes(videoFile.bytes!);
return BytesFile(
"thumb.png",