Giter Site home page Giter Site logo

dreampowder / flutter_social_textfield Goto Github PK

View Code? Open in Web Editor NEW
23.0 23.0 20.0 17.82 MB

A TextEditingController for providing common social media text contents.

License: BSD 3-Clause "New" or "Revised" License

Kotlin 0.26% Swift 2.38% Objective-C 0.08% Dart 94.19% HTML 3.09%
dart flutter text-editing-controller

flutter_social_textfield's People

Contributors

berk-byrktr avatar dreampowder avatar pinguluk avatar sippetrosyan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

flutter_social_textfield's Issues

How to customized some link pattern?

Hi, this lib is useful.

I want customized the link pattern, is there a way to do it?

am using this logic to buld spans:

class WidgetSpanTextEditingController extends TextEditingController {
  WidgetSpanTextEditingController({String? text})
      : super.fromValue(text == null
            ? TextEditingValue.empty
            : TextEditingValue(text: text));

  @override
  TextSpan buildTextSpan(
      {required BuildContext context,
      TextStyle? style,
      required bool withComposing}) {
    TextRange? matchedRange;
    TextRange? matchedRange2;

    if (text.contains('$SPLIT_CMD_START') && text.contains('$SPLIT_CMD_END')) {
      matchedRange = _findMatchedRange(text);
      matchedRange2 = _findMatchedRange(text, trimTag: false);
    }

    if (matchedRange != null) {
      return TextSpan(
        children: [
          TextSpan(text: matchedRange.textBefore(text)),
          WidgetSpan(
              child: GestureDetector(
                  child: Padding(
                    padding: const EdgeInsets.only(
                        right: 5.0, top: 2.0, bottom: 2.0),
                    child: ClipRRect(
                        borderRadius:
                            const BorderRadius.all(Radius.circular(5.0)),
                        child: Container(
                          padding: const EdgeInsets.all(5.0),
                          color: Colors.orange,
                          child: Row(
                            mainAxisAlignment: MainAxisAlignment.start,
                            mainAxisSize: MainAxisSize.min,
                            children: <Widget>[
                              Text(matchedRange2!
                                      .textInside(text)
                                      .trim()
                                      .split(SPLIT_CMD_MID)[0]
                                  //style: textStyle?.copyWith(color: Colors.orange),
                                  ),
                              const SizedBox(
                                width: 5.0,
                              ),
                              InkWell(
                                child: const Icon(
                                  Icons.close,
                                  size: 15.0,
                                ),
                                onTap: () {
                                  // controller!.value = controller!.value
                                  //     .copyWith(
                                  //         text: controller!.text.replaceRange(
                                  //             start!, start! + text.length, ''),
                                  //         selection: TextSelection.fromPosition(
                                  //             TextPosition(offset: start!)));
                                  clearMatchedText(matchedRange, text);
                                },
                              )
                            ],
                          ),
                        )),
                  ),
                  onTap: () {})),
          TextSpan(text: matchedRange.textAfter(text)),
        ],
        style: style,
      );
    }

    return TextSpan(text: text, style: style);
  }

  String clearMatchedText(TextRange? range, String text) {
    if (range != null) if (range.start >= 0 && range.end <= text.length) {
      text = text.replaceRange(range.start, range.end, '');
      return text;
    }
    return text;
  }

  // TextRange _findMatchedRange(String text) {
  //   final RegExp matchPattern = RegExp(RegExp.escape('\uffff'));
  //   late TextRange matchedRange;

  //   for (final Match match in matchPattern.allMatches(text)) {
  //     matchedRange = TextRange(start: match.start, end: match.end);
  //   }

  //   return matchedRange;
  // }

  TextRange _findMatchedRange(String text, {bool trimTag = true}) {
    final RegExp matchPattern = RegExp(r'<cmd>(.*?)<\/cmd>');
    late TextRange matchedRange;

    for (final Match match in matchPattern.allMatches(text)) {
      int start = match.start;
      if (!trimTag) start += '$SPLIT_CMD_START'.length;
      int end = match.end;
      if (!trimTag) end -= '$SPLIT_CMD_END'.length;
      matchedRange = TextRange(start: start, end: end);
    }
    return matchedRange;
  }
}

but question is when deleting, it is not delete whole span, but character by character.

Issue with skin colours emojis

When texting emojis, and you introduce a skin colour emoji, the emoji after it will be the same as first, and sometimes breaks the TextField State.

Any thoughts on this?

the method .subscribeToDetection is not there anymore?

the method .subscribeToDetection is not there here is some of my code-

void initState() { _subCommentController = SocialTextEditingController()..setTextStyle(DetectedType.mention, TextStyle(color: Colors.purple, backgroundColor: Colors.purple.withAlpha(50))); _streamSubscription = _subCommentController.subscribeToDetection(func); super.initState(); }

and also how do i add suggestions when someone types like it comes in the example gif

Support for types

Hello!

Thank you for this awesome package.

I was wondering if the support for custom types would be added. Right now I'm using all 3 customizable DetectionTypes and may need more in the future. Is there a way to do that, please?

Unable to detect URL with "-" in subdomain

Here https://bhupesh-v.github.io/ is a sample URL that is not being detected

Seems like the regexContent is missing "-" as valid char

const urlRegexContent = "((http|https)://)(www.)?" +
    "[a-zA-Z0-9@:%._\\+~#?&//=]" +
    "{2,256}\\.[a-z]" +
    "{2,6}\\b([-a-zA-Z0-9@:%" +
    "._\\+~#?&//=]*)";

Here's a draft solution

void main() {
  const oldurlRegexContent = "((http|https)://)(www.)?" +
    "[a-zA-Z0-9@:%._\\+~#?&//=]" +
    "{2,256}\\.[a-z]" +
    "{2,6}\\b([-a-zA-Z0-9@:%" +
    "._\\+~#?&//=]*)";
 
// notice the extra "-"
  const newurlRegexContent = "((http|https)://)(www.)?" +
    "[a-zA-Z0-9@:%._\\+~#?&//=-]" +
    "{2,256}\\.[a-z]" +
    "{2,6}\\b([-a-zA-Z0-9@:%" +
    "._\\+~#?&//=]*)";
  
  var f1 = RegExp(oldurlRegexContent);
  var f2 = RegExp(newurlRegexContent);
  print(f1.stringMatch("https://bhupesh-v.github.io/"));
  // null
  print(f2.stringMatch("https://bhupesh-v.github.io/"));
  // https://bhupesh-v.github.io/

}

size.isFinite is not true

this is the reply textfield

  replyField({required this.commentID, required this.docName, Key? key})
      : super(key: key);

  final String commentID;
  final String docName;

  @override
  _replyFieldState createState() => _replyFieldState();
}

class _replyFieldState extends State<replyField> {
  late final CollectionReference users =
      FirebaseFirestore.instance.collection('Users');

  late final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;

  late final FirebaseStorage storage = FirebaseStorage.instance;

  late final CollectionReference posts =
      FirebaseFirestore.instance.collection('Posts');
  late StreamSubscription<SocialContentDetection> _streamSubscription;
  SocialTextEditingController _subCommentController =
      SocialTextEditingController()
        ..setTextStyle(
            DetectedType.mention,
            TextStyle(
                color: Colors.purple,
                backgroundColor: Colors.purple.withAlpha(50)));
  bool isSuggestion = false;
  FocusNode _focusNode = FocusNode();
  ScrollController _scrollController = ScrollController();
  late SocialContentDetection lastDetection;
  @override
  void initState() {
    super.initState();

    _streamSubscription =
        _subCommentController.subscribeToDetection(testFunction);
  }

  void testFunction(SocialContentDetection detection) {
    lastDetection = detection;
  }

  void dispose() {
    _focusNode.dispose();
    _streamSubscription.cancel();
    _subCommentController.dispose();
    _scrollController.dispose();
    super.dispose();
  }

  PreferredSize mention(double height) {
    return PreferredSize(
      child: FutureBuilder<QuerySnapshot>(
          future: users.get(),
          builder: (context, snapshot) {
            switch (snapshot.connectionState) {
              case ConnectionState.waiting:
                return SizedBox.shrink();
              default:
                if (snapshot.hasError) {
                  return Text(snapshot.error.toString());
                } else {
                  var suggestions = snapshot.data!.docs
                      .where((element) => element
                          .get('username')
                          .contains(lastDetection.text.replaceAll("@", "")))
                      .toList();
                  return Container(
                    child: ListView.separated(
                      itemCount: suggestions.length,
                      itemBuilder: (context, index) {
                        return ListTile(
                          title: suggestions[index].get("username"),
                        );
                      },
                      separatorBuilder: (context, index) {
                        return Divider(color: Colors.grey[600]);
                      },
                    ),
                  );
                }
            }
          }),
      preferredSize: Size.fromHeight(height),
    );
  }

  @override
  Widget build(BuildContext context) {
    var height = MediaQuery.of(context).size.height * 0.4;
    return DefaultSocialTextFieldController(
      focusNode: _focusNode,
      scrollController: _scrollController,
      textEditingController: _subCommentController,
      child: Container(
        padding: EdgeInsets.all(8),
        child: Column(
          children: <Widget>[
            Expanded(
              child: TextField(
                focusNode: _focusNode,
                expands: true,
                scrollController: _scrollController,
                scrollPhysics: AlwaysScrollableScrollPhysics(),
                controller: _subCommentController,
                decoration: InputDecoration(
                  suffixIcon: IconButton(
                    icon: Icon(Icons.reply),
                    onPressed: () async {
                        doing things with firebase and etc. etc.
                        setState(() {});
                      }
                    },
                  ),
                  filled: true,
                  hintText: "Reply",
                  prefixIcon: Icon(Icons.messenger, color: Colors.grey[600]),
                  border: OutlineInputBorder(
                    borderSide: BorderSide.none,
                  ),
                ),
                textInputAction: TextInputAction.done,
                minLines: null,
                maxLines: null,
              ),
            ),
          ],
        ),
      ),
      detectionBuilders: {DetectedType.mention: (_) => mention(height)},
    );
  }
}`

the reply textfield is being used in a ListTile'schildren: which is inside an ExpansionTile which is inside a streambuilder here is the full code(its big and from the ExpansionTile() ):


return ExpansionTile(
    textColor:
        Colors.black54,
    iconColor:
        Colors.black54,
    leading:
        CircleAvatar(
      backgroundColor:
          Colors.black,
    ),
    title:
        Row(
      mainAxisAlignment:
          MainAxisAlignment.spaceBetween,
      children: [
        Text(
          commentData[commentIndex]['username'],
        ),
        Text(commentData[commentIndex]['Time']),
      ],
    ),
    subtitle:
        Row(
      mainAxisAlignment:
          MainAxisAlignment.spaceBetween,
      children: <Widget>[
        Text.rich(
          TextSpan(
              text: '',
              children: commentData[commentIndex]['comment'].split(' ').map<InlineSpan>((w) {
                return w.startsWith('@') && w.length > 1
                    ? TextSpan(
                        text: ' ' + w,
                        style: TextStyle(color: Colors.blue),
                        recognizer: TapGestureRecognizer()..onTap = () {},
                      )
                    : TextSpan(text: ' ' + w, style: TextStyle(color: Colors.black));
              }).toList()),
        ),
        commentLikeButton(commentData[commentIndex]["Likes"], list[index]['document_name'], commentID[commentIndex], "")
      ],
    ),
    trailing:
        SizedBox.shrink(),

    children: [
      // reply list
      subcommentData.isEmpty
          ? Text('No Comments Yet')
          : ListView.separated(
              itemCount: subcommentData.length,
              scrollDirection: Axis.vertical,
              shrinkWrap: true,
              separatorBuilder: (context, index) {
                return Divider(
                  color: Colors.grey[600],
                );
              },
              itemBuilder: (context, replyIndex) {
                return Container(
                  height: 50,
                  color: Colors.white,
                  child: ListTile(
                    dense: true,
                    leading: CircleAvatar(
                      backgroundColor: Colors.black,
                    ),
                    title: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
                      Text(subcommentData[replyIndex]['username']),
                      Text(subcommentData[replyIndex]['Time'])
                    ]),
                    subtitle: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        Text.rich(
                          TextSpan(
                              text: '',
                              children: commentData[replyIndex]['comment'].split(' ').map<InlineSpan>((w) {
                                return w.startsWith('@') && w.length > 1
                                    ? TextSpan(
                                        text: ' ' + w,
                                        style: TextStyle(color: Colors.blue),
                                        recognizer: TapGestureRecognizer()..onTap = () {},
                                      )
                                    : TextSpan(text: ' ' + w, style: TextStyle(color: Colors.black));
                              }).toList()),
                        ),
                        commentLikeButton(subcommentData[replyIndex]['Likes'], list[index]['document_name'], commentID[commentIndex], subcommentID[replyIndex])
                      ],
                    ),
                  ),
                );
              }),

      // reply text field

      SizedBox(
        height: 10,
      ),
      replyField(
          commentID: commentID[commentIndex],
          docName: list[index]['document_name']),
    ],
    // trailing: Text(date),
  );

An issue occurred after upgrading flutter 2.2.0

An issue occurred after upgrading flutter 2.2.0

** BUILD FAILED **

Xcode's output:

4
Invalid depfile: /Users/claude/Documents/projects/Lat/Lat/.dart_tool/flutter_build/de53b0890724bc58734a7ab9fa32f191/kernel_snapshot.d
../../../../.pub-cache/hosted/pub.dartlang.org/flutter_social_textfield-0.0.3/lib/controller/social_text_editing_controller.dart:94:12: Error: The method 'SocialTextEditingController.buildTextSpan' has fewer named arguments than those of overridden method 'TextEditingController.buildTextSpan'.
TextSpan buildTextSpan({TextStyle? style, required bool withComposing}) {
^
../../../../fvm/versions/2.2.0/packages/flutter/lib/src/widgets/editable_text.dart:192:12: Context: This is the overridden method ('buildTextSpan').
TextSpan buildTextSpan({required BuildContext context, TextStyle? style , required bool withComposing}) {
^
../../../../.pub-cache/hosted/pub.dartlang.org/flutter_social_textfield-0.0.3/lib/controller/social_text_editing_controller.dart:94:12: Error: The method 'SocialTextEditingController.buildTextSpan' doesn't have the named parameter 'context' of overridden method 'TextEditingController.buildTextSpan'.
TextSpan buildTextSpan({TextStyle? style, required bool withComposing}) {
^
../../../../fvm/versions/2.2.0/packages/flutter/lib/src/widgets/editable_text.dart:192:12: Context: This is the overridden method ('buildTextSpan').
TextSpan buildTextSpan({required BuildContext context, TextStyle? style , required bool withComposing}) {

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.